Introduction
AngularJS is an undisputed champion among all the front end web development frameworks, well I am no expert to say that since honestly I have not worked much with other popular libraries like Knockout, backbone, amberJS etc. but for last few months I have been exploring the AngularJS and I am loving it.
AngularJS brings several goodies to the table, it's very well designed, robust and well thought framework. In my opinion if you are a web developer, it’s totally worth it to invest your time learning angularJS even if you do not work much on the front end and do not plan to use it in your project in near future.
Background
With the announcement in ng-conf about angularJS and TypeScript collaboration and “Angular 2: built on typescript” anouncement, there seems to be a lot of momentum shift in the developers community from JavaScript to TypeScript for angularJS projects. I am no advocate of TypeScript and also a big fan of JavaScript however in this article I’ll show you how to get started with angularJS using Typescript, avoiding any comparison between TypeScript and JavaScript.
What are we going to achieve at the end of this article?
We are going to build a very simple AngularJS application using TypeScript. This application will have one view with a button named “Get favorite tracks”. On button click it will show a list of tracks, simple enough isn’t it?
I will use visual studio 2013 for creating the demo application, and won’t use any REST API to keep it short.
Note: Although the attached solution is created in VS 2013, it’s really not needed, you can download the attached code and just copy the code inside solution (excluding the .sln and .proj files of course) and it should work without any modification with any IDE.
Enough talk, let’s get started.
Creating project structure
Open visual studio and create and empty web projecct. Delete the web.config file, we do not need it. Use nuget package manager to add following references, you can even add these directly, nuget is optional.
- AngularJS.Core
- AngularJS.Route
- angularjs.TypeScript.DefinitelyTyped (this is for TypeScript definitions of AngularJS)
Optionally you can add bootstrap to style your app.
Create a folder named "app" in the root of the project, this is where we will add our application's code.
Creating Interfaces
In our app, we are going to use an AngularJS service which will communicate with the backend REST API in order to fetch tracks list. I like to keep all the interfaces definitions separate from main business logic, so let’s create another folder inside "app" named “interfaces”, inside this folder create a blank typescript file and add following code in it.
module angularWithTS.Interfaces {
export interface IPlaylistService {
getPlayList: () => Array<ITrack>;
}
export interface ITrack {
title: string;
artist: string;
rating: number;
}
}
Here we have created a typescript module named “angularWithTS.Interfaces”, please note that typescript module is more like the namespace in other programming languages and it has nothing to do with the angularJS module, however you will see later that I have named angular module “angularWithTS” which is just my personal style of naming things but you can definitely name it whatever you like.
You can see I have added two interface definitions in this file. Interface “IPlaylistService” has one method defined name “getPlayList” and method signature tells that it does not take any parameter and returns an Array of ITrack.
ITrack is nothing but another interface defined in the same file which defines the contract of our track objects, using interfaces ensures that no random objects are passed around in our application and saves us from unexpected bugs in our code. This is one of the most shout features of TypeScript.
Implementing Interfaces
So you have defined the service interface “IPlaylistService” now let’s implement it. Create another folder inside "app" folder and name it “services”, Now add a new typescript file named “playlistService.ts” add the following code in this file.
module angularWithTS.Services {
export class PlayListService implements angularWithTS.Interfaces.IPlaylistService {
httpService: ng.IHttpService
static $inject = ["$http"];
constructor($http: ng.IHttpService) {
this.httpService = $http;
}
getPlayList = () => {
var res: Array<angularWithTS.Interfaces.ITrack> = [
{ title: "Numb", artist: "Linkin Park", rating: 5 },
{ title: "Fire Flies", artist: "Owl City", rating: 4.3 },
{ title: "Yellow", artist: "coldplay", rating: 4.5 },
{ title: "Skyfall", artist: "Adele", rating: 4.5 }
];
return res;
}
}
angular.module("angularWithTS").service("angularWithTS.Services.PlayListService", PlayListService);
}
as you can see in the above code, we are encapsulating our service inside another namespace “angularWithTS.Services”. Also “export” keyword ensures that our service will be available to be called from “angularWithTS.Services” module and is not just local to module.
To create a service we create a new class which implements the “IPlaylistService” interface. There are 5 different ways to create services in an AngularJS application namely (service, factory, provider, value and constant), all with their own use cases and limitations. AngularJS calls them recipes to create services and one of the recipe name is service that’s I have chosen for the purpose of this demo.
I know service recipe to create service is really confusing but since AngularJS team did not consult us for naming things we will try to live with it. You can find more information about the services in AngularJS developer’s guide.
Our service needs to talk to the backend APIs and hence needs angularJS service “$http”. We can specify this dependency in the service constructor which angular will fulfil at run time.
Please note that function’s parameter is named “$http” and it’s type is specified as “ng.IHttpService”, however to make your service class servive through minification and compression process of popular front end tools like Grunt and Gulp, you need to do one addition step and shown below.
static $inject = ["$http"];
$inject is an special property which angularJS framework will consume, make sure it’s marked as “static”, it’s value is an array of strings, and each string is an identifies for a particular service, also the order in which strings are added in an array is important, this order should be the same as constructor parameters order. AngularJS will check this property at runtime to determine what are the services it needs to inject.
You can also see our service does implement the “getPlayList” function from the “IPlaylistService” interface, however we are not actually calling any REST apis rather returning the hard coded values, but I believe you got the Idea.
angular.module("angularWithTS").service("angularWithTS.Services.PlayListService", PlayListService);
previous line is standard AngularJS code which adds our class to angularJS module as a service. We are yet to create the angular module for our app so lets create one next.
Creating the AngularJS module
To create the module add “app.module.ts” file in “app” folder, I like to name module file like this but again it’s just personal preference. Inside “app.module.ts” file add the following code:-
((): void=> {
var app = angular.module("angularWithTS", ['ngRoute']);
app.config(angularWithTS.Routes.configureRoutes);
})()
we have added IIFE and used typescript lambda function to define our code. Inside that it’s all standard angular code, where in the first line we are creating an angularJS module named “angularWithTS” which has dependency on ‘ngRoute’ module, in the next line we are configuring our app with routing information by providing a function reference which is defined in “angularWithTS.Routes” class which we have not yet created so let’s quickly create that.
Defining the routes
Add another file name “app.routes.ts” in “app” folder, and add following code in it:-
module angularWithTS {
export class Routes {
static $inject = ["$routeProvider"];
static configureRoutes($routeProvider: ng.route.IRouteProvider) {
$routeProvider.when("/home", { controller: "angularWithTS.controllers.tsDemoController", templateUrl: "/app/views/playlist.html", controllerAs: "playList" });
$routeProvider.otherwise({ redirectTo: "/home" });
}
}
}
Code above is pretty straight forward however I would like to highlight one thing that is “controllerAs” parameter for the first route registered. Value provided for this “playList” says, controller bound to the route will be accessible with “playList” alias inside the view for this route.
This is important to note, since you can access controllers property/methods with the help of this alias rather relying on the “$scope” of that controller. We have mapped “angularWithTS.controllers.tsDemoController” controller to our route which we are going to create next.
Creating the controller
Let’s create the controller by adding a new folder inside “app” folder named “controllers” and add a new typescript file name “tsDemoController.ts” and add the following code.
module angularWithTS.controllers {
export class TSDemoController {
playListService: angularWithTS.Interfaces.IPlaylistService;
static $inject = ["angularWithTS.Services.PlayListService"];
constructor(playListService: angularWithTS.Interfaces.IPlaylistService) {
this.playListService = playListService;
}
favorites: Array<angularWithTS.Interfaces.ITrack>;
getFavourites = () => {
this.favorites = this.playListService.getPlayList();
}
}
angular.module("angularWithTS").controller("angularWithTS.controllers.tsDemoController", TSDemoController);
}
We have created a new class for out controller named “TSDemoController” and have specified it’s dependency on “angularWithTS.Services.PlayListService” service in the constructor. Notice the “$inject” property again.
Our controller has a property named “favorites” which has a type of “Array<angularWithTS.Interfaces.ITrack>” we will use this property to bind our view. It also has a function named “getFavourites” which simply populate the “favorites” property with the result of service call. We will use this function as a click event handler in our view. Next we will create the view.
Creating the view
To add view to our project add another folder named “views” in “app” folder and add an empty html file “playlist.html”. Add the following code in the html file:
<div class="jumbotron">
<ul class="list-group">
<li ng-show="playList.favorites" class="list-group-item" ng-repeat="t in playList.favorites">
{{t.title}}
</li>
</ul>
<button class="btn btn-block" ng-click="playList.getFavourites()">Get Favorites Tracks</button>
</div>
As you can see we are accessing controller property “favorites” via alias like “playList.favorites” and not relying on “$scope”. Rest of it is self-explanatory.
Bringing it all together
Finally we have almost completed the application, lets add a shell “Index.html” file in the “app” as shown below, make sure you have all the third party scripts and CSS files added in your project which are referenced in the Index.html.
<!DOCTYPE html>
<html ng-app="angularWithTS">
<head>
<title>AngularJS With TypeScript</title>
<link href="content/css/bootstrap.min.css" rel="stylesheet" />
<link href="content/css/bootstrap.custom.min.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<div class="page-header">Click the following button to get your favorite tracks.</div>
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div ng-view="">
</div>
</div>
</div>
</div>
<script src="scripts/angular.js"></script>
<script src="scripts/angular-route.js"></script>
<script src="app/app.routes.js"></script>
<script src="app/app.module.js"></script>
<script src="app/services/playlistService.js"></script>
<script src="app/controllers/tsDemoController.js"></script>
</body>
</html>
Conclusion
I have barely scratched the surface in this article. Of course one article cannot do the justice to either of AngularJS or TypeScript. Hopefully it helps you to get started with TypeScript in angularJS project.