Underscore.JS: Access local variables and function inside each function of UnderscoreJS in Typescript

Standard

While using AngularJS with Typescript, I witnessed the scenario, where my function is accessing variables and functions defined in controller constructor, to make life easy for some collection manipulation, I used underscoreJS helper functions, _.each

_.each(data, function(item : any) {
     item.name = this.formatName(item);
});

Everything was on track, except I was unable to access local variables and functions(this.formatName) defined in the angular controller in underscoreJS _.each, I was getting an undefined error for this.formatName method inside _.each.

Solution:

As you are aware of the fact that ‘this‘ keyword behaves differently in JavaScript compared to other languages. For Typescript, ‘this‘ keyword refers to the current instance of the class. In JavaScript, the value of ‘this‘ is determined mostly by the invocation context of the function and where it is called. (Read this reference)

  1. In order to resolve this problem, inject ‘this‘ as context by setting it up as the third parameter to _.each helper function
_.each(data, function(item : any) {
     item.name = this.formatName(item);
}, this);

2. There is one more solution too by saving the context in temporary variable before consuming ‘this’ in _.each

public testFunction() : void {
  var context = this;
  _.each(data, function(item : any) {
      item.name = context.formatName(item);
  });
}

 

AngularJS: Error – Argument of type ‘RegExp’ is not assignable to parameter of type ‘string’

Standard

While working on a test project in AngularJS with Typescript, I came across this specific error:

Argument of type 'RegExp' is not assignable to parameter of type 'string'

Code:

let expression = new RegExp(/(test)=\(A:([0-9.]*)\)/g);

The problem is that I am duplicating the initialization process of Regex, first, it set up regular expression by standard syntax (/ /g) and then re-setup regular expression using constructor ( new RegExp() )

Solution 1:

let expression = /(test)=\(A:([0-9.]*)\)/g;

Solution 2:

let expression = new RegExp('(test)=\(A:([0-9.]*)\)', 'g');

 

Angular2: Resolve TS5023 Build:Unknown compiler option ‘typeRoots’ and ‘types’ error in Visual Studio

Standard

I installed fresh copy of Visual Studio 2015 update 3 on my secondary machine and try to execute Angular2 project integrated with ASP.NET CORE and came across an issue

TS5023 Build:Unknown compiler option 'typeRoots'
TS5023 Build:Unknown compiler option 'types'

Solution 1:

In VS2015 go to:

Tools -> Options -> Projects and Solutions -> External Web Tools.

uncheck anything that starts with $(VSINSTALLDIR)

Solution 2 (recommended):

Before trying any other option, I just updated Typescript version installed for VS2015 from 2.0.3 to 2.2.0 and this resolve compiler option error, Upgrade to latest version of Typescript by installing executable from MS Official Download link.

Angular2: Import Lodash into Angular2 application using Typescript

Standard

Follow below steps to add lodash into your Angular2 app using Typescript 2.0.

Please note I am using Typescript 2.0 and Angular 2.0

Step 1:

Include lodash as the dependencies in package.json file by CMD

> npm install --save lodash

Step 2:

Include lodash typings as the dev dependencies in package.json file by CMD for Typescript otherwise you will get error in Visual Studio/Visual Code that “Cannot find module ‘lodash’

> npm install --save-dev @types/lodash

After above commands your package.json will be updated as below:

screenshot_1

Step 3:

As I am using systemJS to load module, so Update the systemjs.config.js file to map lodash. This file provides information to a module loader about where to find application modules, and registers all the necessary packages.

map: {
        'lodash': 'npm:lodash'
     },
        
packages: {
            lodash: {
                main: 'index.js',
                defaultExtension: 'js'
            }
          }

Step 4:

Import lodash in your angular component.

import * as _ from "lodash";

Please note that If you use import _ from “lodash”, you will receive following error that “Module ‘lodash’ has no default export”:

screenshot_3

Step 5:

Use lodash in your code as per need just like below as I am using _.isEqual(a,b) and IntelliSense is working fine:

screenshot_4

Update:

Lot of things have been changed since Angular2 RTM, I was having issues like “cannot find namespace ‘_’ and Cannot find name ‘many’ ”

Solution: Update @types/lodash in package.json to Typescript 2.0 compatible types in order to use lodash without any issue

"@types/lodash": "ts2.0"

Visit this link for complete solution:

Cannot find name ‘many’ and namespace ‘_’ issues with Lodash in Angular2 application using Typescript

 

Cheers. Happy Coding.

Integration of Typescript in AngularJS

Standard

GitHub Repository:  GitHub Link

AngularJS is a JavaScript Model-View-Whatever (MVW) framework that allows the writing of dynamic Single Page web Applications, and is becoming widely embraced because of its simplicity and completeness. Amongst its exhaustive list of features, the framework includes Dynamic Data Binding, Client-side Routing, Unit and End2End Testing or even HTML language extension.

Typescript, on the other hand, is a typed superset of the JavaScript language that is compiled to produce clean, simple cross-browser compatible JavaScript code, which can help when building and maintaining large-scale applications. Typescript follows ECMAScript 5 (ES5) syntax but also includes several proposed features of ES6 such as interfaces, classes and modules.

Please install Web Essentials for VS 2013 for rending JS file for TS on side by side while typing

https://visualstudiogallery.msdn.microsoft.com/56633663-6799-41d7-9df7-0f2a504ca361

Migrating AngularJS to Typescript

Entity class

2015-10-29_10-43-25

module App.Entity {
    "use strict";

    export interface ICandidate {
        candidate_id: number;
        first_name: string;
        middle_initial: string;
        last_name: string;
        email: string;
        expected_salary: number;

    };
}

Defining Application Modules

2015-10-29_10-49-17

((): void => {
    "use strict";

    angular
        .module("onBoardingApp", [
            "onBoardingApp.Core",
            "onBoardingApp.Module"
        ]);

    angular
        .module("onBoardingApp.Core", [
            "ui.router",
            "oc.lazyLoad",
            "breeze.angular"
        ]);

    angular
        .module("onBoardingApp.Module", [
            "onBoardingApp.home",
        // "onBoardingApp.candidate",
            "onBoardingApp.layout"
        ]);
})();

Defining Application Configuration

2015-10-29_10-57-49

((): void => {
    "use strict";

    angular
        .module("onBoardingApp")
        .config(config);

    config.$inject = ["$stateProvider"];

    function config($stateProvider: ui.IStateProvider): void {
        $stateProvider
            .state("adminHome", {
                url: "/",
                controller: "HomeController",
                controllerAs: "vm",
                templateUrl: "/Scripts/appTS/modules/home/views/onBoardingApp.home.html",
                resolve: {
                    getSecondaryCandidateService: getSecondaryCandidateService
                }
            })
            .state("allCandidates", {
                url: "/candidates",
                controller: "CandidateController",
                controllerAs: "vm",
                templateUrl: "/Scripts/appTS/modules/candidate/views/onBoardingApp.candidate.html",
                resolve: {
                    allCandidates: function ($ocLazyLoad: oc.ILazyLoad): any {
                        return $ocLazyLoad.load([
                            {
                                name: "onBoardingApp.candidate",
                                files: ["/Scripts/appTS/modules/candidate/onBoardingApp.candidate.js"],
                                cache: false
                            },
                            {
                                name: "onBoardingApp.candidate.controllers",
                                files: ["/Scripts/appTS/modules/candidate/js/onBoardingApp.candidate.controller.js"],
                                cache: false
                            },
                            {
                                name: "onBoardingApp.candidate.services",
                                files: ["/Scripts/appTS/modules/candidate/js/onBoardingApp.candidate.services.js"],
                                cache: false
                            }
                        ]);
                    }
                }
            });

        function getSecondaryCandidateService(homeService: App.Home.IHomeService): ng.IPromise<App.Entity.ICandidate[]> {
            return homeService.getAllSecondaryCandidates();
        }
    }

})();

Defining Shared Services

2015-10-29_11-47-00

module App.Service {
    "use strict";

    export interface IWebApi {
        withParameter: (methodType: string, webApiUrl: string, parameterObject: any) => any;
        nonParameter: (methodType: string, webApiUrl: string) => any;
    }

    webApi.$inject = ["$http", "$templateCache", "$q"];

    function webApi($http: ng.IHttpService, $templateCache: ng.ITemplateCacheService, $q: ng.IQService): IWebApi {

        var factory: IWebApi = {
            withParameter: withParameter,
            nonParameter: nonParameter
        };

        return factory;

        function withParameter(methodType, webApiUrl, parameterObject) {
            var deferred = $q.defer();
            $http({
                method: methodType,
                url: webApiUrl, 
                data: parameterObject,
                cache: $templateCache
            })
                .success(deferred.resolve)
                .error(deferred.resolve);
            return deferred.promise;
        }

        function nonParameter(methodType, webApiUrl) {
            var deferred = $q.defer();
            $http({
                method: methodType,
                url: webApiUrl,
                cache: $templateCache
            })
                .success(deferred.resolve)
                .error(deferred.resolve);
            return deferred.promise;
        }
    }

    angular
        .module("onBoardingApp")
        .factory("webApi", webApi);
}

Defining Module specific services

2015-10-29_12-05-16

module App.Home {
    "use strict";

    export interface IHomeService {
        getAllSecondaryCandidates: () => ng.IPromise<App.Entity.ICandidate[]>;
    }

    HomeService.$inject = ["webApi"];

    function HomeService(webApi: App.Service.IWebApi): IHomeService {

        var factory: IHomeService = {
            getAllSecondaryCandidates: getAllSecondaryCandidates
        };

        return factory;

        function getAllSecondaryCandidates() {
            return webApi.nonParameter('GET', '/Home/GetSecondaryCandidateData');
        }
    }

    angular
        .module("onBoardingApp.home")
        .factory("homeService", HomeService);
}

Defining Module Controller

2015-10-29_12-32-20

module App.Candidate {
    "use strict";

    interface ICandidateController {
        candidates: App.Entity.ICandidate[];
        lazyLoadParams: string[];
        tempValueForUnitTest: string;
        showAction: boolean;
        currentEdit: App.Entity.ICandidate;
        itemToEdit: App.Entity.ICandidate;
        newCandidate: App.Entity.ICandidate;
        activate: () => void;
        getCandidates: () => ng.IPromise<App.Entity.ICandidate[]>;
        findCandidate: (id: number) => number;
        add: () => void;
        edit: (candidate: App.Entity.ICandidate) => App.Entity.ICandidate;
        cancelEdit: (candidateId: number) => void;
        save: (candidate: App.Entity.ICandidate) => App.Entity.ICandidate[];
        testFunctionForUnitTesting: () => string;

    }

    class CandidateController implements ICandidateController {

        candidates: App.Entity.ICandidate[];
        lazyLoadParams: string[];
        tempValueForUnitTest: string;
        showAction: boolean;
        currentEdit: App.Entity.ICandidate;
        itemToEdit: App.Entity.ICandidate;
        newCandidate: App.Entity.ICandidate;

        static $inject: string[] = ["candidateService"];

        constructor(private candidateService: ICandidateService) {
            this.activate();
        }

        activate() : void {
            this.lazyLoadParams = [
                '/Scripts/appTS/shared/directive/candidateGrid.js',
                '/Scripts/appTS/shared/directive/maxLengthForNumbers.js',
                '/Scripts/appTS/shared/directive/validemail.js'
            ];
            this.tempValueForUnitTest = "testing123";
            this.showAction = true;
            this.itemToEdit = <App.Entity.ICandidate>{};
            this.currentEdit = <App.Entity.ICandidate> {};
            this.getCandidates();
            
        }

        getCandidates(): ng.IPromise<App.Entity.ICandidate[]> {
            return this.candidateService.getAllCandidates()
                .then((response: ng.IHttpPromiseCallbackArg<App.Entity.ICandidate[]>): App.Entity.ICandidate[] => {
                    this.candidates = <App.Entity.ICandidate[]>response
                    return this.candidates;
                });
        }

        findCandidate = (id: number): number => {
            for (var i in this.candidates) {
                if (this.candidates[i].candidate_id == id)
                    return i;
            }
        };

        add(): void {
            this.candidates.push(this.newCandidate);
            this.newCandidate = null;
        };

        edit = (candidate: App.Entity.ICandidate): App.Entity.ICandidate => {
            this.currentEdit[candidate.candidate_id] = true;
            this.itemToEdit = angular.copy(candidate);
            return this.itemToEdit;
        };

        cancelEdit = (candidateId: number): void => {
            console.log(this)
            this.currentEdit[candidateId] = false;
        };

        save = (candidate: App.Entity.ICandidate): App.Entity.ICandidate[] => {
            var c = this.findCandidate(candidate.candidate_id);
            this.candidates[c] = this.itemToEdit;
            this.currentEdit[candidate.candidate_id] = false;
            return this.candidates;
        };

        testFunctionForUnitTesting(): string {
            return "test";
        }
    }

    angular
        .module("onBoardingApp.candidate")
        .controller("CandidateController", CandidateController);
}

Defining Directive consists of DOM manipulation logic

2015-10-29_12-57-18

module App.Directive {
    "use strict";

    interface Ivalidmail extends ng.IDirective {
    }

    interface IvalidmailScope extends ng.IScope {
    }

    interface IvalidmailAttributes extends ng.IAttributes {
    }

    validmail.$inject = ["$window"];

    function validmail($window: ng.IWindowService): Ivalidmail {
        return {
            restrict: 'A',
            scope: {},
            controller: validEmailController,
            controllerAs: 'vm',
            link: link,
            bindToController: true,
        }

        function link(scope: IvalidmailScope, element: ng.IAugmentedJQuery, attrs: IvalidmailAttributes) {
            element.bind('blur', (): void => {
                var regex = /\S+@\S+\.\S+/;
                element.css('background-color', !regex.test(element.val()) ? 'yellow' : '');
            });
        }

        function validEmailController() {
            var vm = this;

        }
    }

    angular
        .module("onBoardingApp")
        .directive("validEmail", validmail);
}

Defining Directive consists of business logic

2015-10-29_11-29-43

module App.Directive {
    "use strict";

    interface IcandidateGridScope extends ng.IScope {
        vm: any;
        datasource: App.Entity.ICandidate[];
        showaction: boolean;
        itemtoedit: any;
        currentedit: any;

    }

    interface IcandidateGridAttributes extends ng.IAttributes {
    }

    interface ICandidateGridController {
        candidates: App.Entity.ICandidate[];
        itemToEdit: App.Entity.ICandidate;
        currentEdit: App.Entity.ICandidate;
        activate(): void;
        cancelEdit(candidateId: number): void;
        edit(candidate: App.Entity.ICandidate): void;
        save(candidate: any): void;
    }

    class CandidateGridController implements ICandidateGridController {

        candidates = [];
        itemToEdit = <App.Entity.ICandidate>{};
        currentEdit = <App.Entity.ICandidate>{};

        static $inject = ['$scope'];

        constructor(public $scope: IcandidateGridScope) {
            this.activate();
        }

        activate(): void {
            this.candidates = this.$scope.vm.datasource;
            this.currentEdit = this.$scope.vm.currentedit;
        }

        cancelEdit(candidateId: number): void {
            this.$scope.vm.canceledit()(candidateId);
        };

        edit(candidate: App.Entity.ICandidate): void {
            this.itemToEdit = this.$scope.vm.editcandidate()(candidate);
        };

        save(candidate: App.Entity.ICandidate): void {
            this.candidates = this.$scope.vm.savecandidate()(candidate);
        };
    }

    export class candidateGrid implements ng.IDirective {
        static instance(): ng.IDirective {
            return new candidateGrid;
        }

        restrict = 'EA';
        templateUrl = 'scripts/app/shared/directive/template/candidateGrid.html';
        scope = {
            datasource: '=',
            currentedit: '=',
            itemtoedit: '=',
            showaction: '@',
            canceledit: '&',
            editcandidate: '&',
            savecandidate: '&',
            addcandidate: '&'
        };
        controller = CandidateGridController;
        controllerAs = 'vm';
        bindToController = true;
        transclude = true;

        link(scope: IcandidateGridScope, element: ng.IAugmentedJQuery, attrs: IcandidateGridAttributes) {

        };

    }

    angular
        .module("onBoardingApp")
        .directive("candidateDataGrid", candidateGrid.instance);
}