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

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s