When Two Forces Meet (AngularJS, TypeScript)

AngularJS is largely growing in popularity among front-end developers. According to a JavaScript developer survey conducted in 2013, it was shown that AngularJS was among the top two most used JavaScript frameworks. In a world where there are close to 17 million MVC JavaScript frameworks, this puts AngularJS on top of the game. On the other end of the scale, while not as widely used as AngularJS, TypeScript is slowly becoming the de facto compiler language for writing object oriented JavaScript. So what happens when these two forces meet? A great power emerges! We learned from Uncle Ben (Spider-Man movie) that with great power, comes great responsibility. In this tutorial, I show how these two forces can be combined to produce an ultimate web application.

Capture

Setup

As a .NET developer, I use Visual Studio 2013 and the TypeScript plugin for Visual Studio. This is what I recommend that you use unless you are for some reason against the Windows platform. Then using the NuGet packet manager, you can install the necessary AngularJS core files. In addition to this, make sure to install the AngularJS TypeScript definitely typed files which provides typed AngularJS components that can be used in your TypeScript code.

Bootstrapper

In AngularJS you create an app.js where you load your modules, controllers, factories and directives. You do almost the same thing in TypeScript, the only difference here is that your controllers, factories and directives are represented as TypeScript classes in an app.ts file. So your app.ts can look like this:

var appModule = angular.module("myApp", []);

appModule.controller("MyController", ["$scope", "MyService", ($scope, MyService)
    => new Application.Controllers.MyController($scope, MyService)]);

appModule.factory("MyService", ["$http", "$location", ($http, $location)
    => new Application.Services.MyService($http, $scope)]);

appModule.directive("myDirective", ()
    => new Application.Directives.MyDirective());

Note the usage of lambda, we do this to reserve lexical scope. Always make sure to use this instead of function() in TypeScript. Now that you’ve set up your bootstrapper, in the next sections we’ll look at how the individual AngularJS components are written in TypeScript.

Controller Classes

Controllers are written as classes, so your MyController.ts class can look like this:

module Application.Controllers {

    import Services = Application.Services;

    export class MyController {

        scope: any;
        myService: Services.IMyService;
	    data: any;
		
        constructor($scope: ng.IScope, myService: Services.IMyService) {
            this.scope = $scope;
            this.myService = myService;
	        this.data = [];
        }

        private GetAll() {
            this.myService.GetAll((data) => {
                this.data = data;
            });
        }
	}
}

Factory Classes

Similarly, factories or services are written as classes. So your MyService.ts class can look like this:

module Application.Services {

    export interface IMyService {
        GetAll(successCallback: Function);
    }

    export class MyService {

        http: ng.IHttpService;
        location: ng.ILocationService;

        constructor($http: ng.IHttpService, $location: ng.ILocationService) {
            this.http = $http;
            this.location = $location;
        }

        GetAll(successCallback: Function) {
            this.http.get(this.location.absUrl()).success((data, status) => {
                successCallback(data);
            }).error(error => {
                successCallback(error);
            });
        }
	}
}

Note the interface IMyService here. Always use interfaces to abstract your classes in TypeScript, just as you would usually do in a typed language.

Directive Classes

Directives are also written as classes. So your MyDirective.ts class can look like this:

module Application.Directives {

    export class MyDirective {

        constructor() {
			return this.CreateDirective();
        }

        private CreateDirective():any {
            return {
                restrict: 'E',
                template: '<div>MyDirective</div>
            };
        }
    }
}

Databinding with Alias

Finally, to be able to use your TypeScript classes from your HTML, you need to databind using alias. So your HTML can look like this:

<html data-ng-app="myApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<body>
	<div data-ng-controller="MyController as mc">
		<div data-ng-repeat="element in mc.data">
			<label>{{element}}</label>
		</div>
		<my-directive></my-directive>
	</div>
</body>
</html>

Databinding without Alias

Unfortunately, databinding with alias does not work in Internet Explorer 8. Neither do directive elements(!). To make it work in IE 8, you need to change your HTML so it looks like this:

<html xmlns:ng="http://angularjs.org" id="ng-app" data-ng-app="myApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--[if lte IE 8]>
            <script>
                document.createElement('ng-include');
                document.createElement('ng-pluralize');
                document.createElement('ng-view');
                document.createElement('my-directive');
                document.createElement('ng:include');
                document.createElement('ng:pluralize');
                document.createElement('ng:view');
            </script>
            <script src="libs/es5-shim/es5-shim.js"></script>
            <script src="libs/JSON/json3.js"></script>
        <![endif]-->
</head>
<body>
	<div data-ng-controller="MyController">
		<div data-ng-repeat="element in data">
			<label>{{element}}</label>
		</div>
		<my-directive></my-directive>
	</div>
</body>
</html>

Now if you want to call your TypeScript class methods from your HTML in IE 8 without an alias, then you need to hook your methods (and everything else they use) onto the Angular scope. This can be done like the following, in MyController.ts:

module Application.Controllers {

    import Services = Application.Services;

    export class MyController {

        scope: any;
		
        constructor($scope: ng.IScope, myService: Services.IMyService) {
            this.scope = $scope;
            this.scope.myService = myService;
			this.scope.data = [];
			this.scope.GetAll = this.GetAll;
        }

        GetAll() {
            this.scope.myService.GetAll((data) => {
                this.scope.data = data;
            });
        }
	}
}

Notice how everything is hooked onto scope. That way, databinding is correctly achieved in IE 8, and you are able to call your GetAll() method from the HTML by simply typing GetAll() without alias.

Hope you enjoyed this tutorial! I’m planning on holding a workshop about this and doing TDD at this year’s Norwegian Developers Conference 2014, so make sure to book your tickets! 🙂

Anonymous Functions in JavaScript are Evil

Anonymous functions in JavaScript can cause a lot of headache and pain. If you want to write testable JavaScript, you will spend days scratching your head to find a way to assert logic that resides in an anonymous function. The problem is that we have no control over what happens inside the anonymous function, unless we stub the sucker out and choose whatever happens after the function is called. But that’s not really a smooth way to do it, is it? There are ways to get around this issue, but some of them are messy in my opinion. There isn’t really a perfect way to do it, but I will show you two different ways that I know are possible.

Consider studentsController.js which has a function getStudents() that calls an injected service studentService:

var getStudents = function() {
    var _this = this;
    this.studentService.getAll(function (data) {
	_this.handle(data);
    });
};

We are testing the getStudents() function, how do we assert that the handle() function was called or that the data was updated correctly? It’s happening in an anonymous function call.

“Testing hooks”

One way to do it is to use so called “testing hooks”. This is in my opinion a messy way, since you have to insert code into the logic just to handle testing. What is possible however, is to make a post script that removes all testing hooks prior to production release. That’s still extra work to do though. You attach a hook onto the studentService.getAll() function like the following (line 6):

var getStudents = function() {
    var _this = this;
    this.studentService.getAll(function (data) {
	_this.handle(data);
    });
    arguments.callee.anonymous = this.studentService.getAll;
};

Now, in your test you need to call getStudents() once. Then you can control the data that goes inside the anonymous function like so:

getStudents.anonymous(data)

Stubbing Your Way Down the Dependency Rabbit Hole

Another possible way is to literally stub your way down the dependency hierarchy and spy on dependencies that get called. This is a painful way. You need an API to create fake objects, assume that we use Jasmine. In this case, you mock an object – http – in your test that gets called by studentService.getAll(), like so:

var httpObject = jasmine.createSpyObj('http', ['get']);
httpObject.get.andReturn("data");

Then you call getStudents() once and expect that the mock was called:

expect(getStudents()).toBe("data");

There are probably other ways to get around this, but I have found that option one is the simplest way. I’m also quite comfortable with it, as long as those hooks get removed pre-production. 🙂