AngularJS with TypeScript
= AngularJS with TypeScript =
== AngularJS with TypeScript ==
http://sirars.com/2014/01/28/when-two-forces-meet-angularjs-typescript/
## 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:
``` javascript
var appModule = angular.module("myApp", []);
appModule.controller("MyController", ["$scope", ($scope)
=> new Application.Controllers.MyController($scope)]);
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:
``` javascript
module Application.Controllers {
import Services = Customer.CreateCustomer.Services;
export class MyController {
scope: any;
customerService: Services.ICustomerService;
data: any;
constructor($scope: ng.IScope, customerService: Services.ICustomerService) {
this.scope = $scope;
this.customerService = customerService;
this.data = [];
}
private GetAll() {
this.customerService.GetAll((data) => {
this.data = data;
});
}
}
}
```
## Factory Classes ##
Similarly, factories or services are written as classes. So your MyService.ts class can look like this:
``` javascript
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:
``` javascript
module Application.Directives {
export class MyDirective {
constructor() {
return this.CreateDirective();
}
CreateDirective() {
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
<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
<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:
``` html
module Application.Controllers {
import Services = Customer.CreateCustomer.Services;
export class MyController {
scope: any;
constructor($scope: ng.IScope, customerService: Services.ICustomerService) {
this.scope = $scope;
this.scope.customerService = customerService;
this.scope.data = [];
this.scope.GetAll = this.GetAll;
}
GetAll() {
this.scope.customerService.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.