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'
            }
          }

screenshot_1

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

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);
}