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