AngularJS and TypeScript

A journey through our stack

Who we are

Santi Albo
@Curial

Jamie McCrindle
@foldr


www.brandnetworksinc.com

Our Front End Stack

AngularJS
TypeScript
Yeoman
IntelliJ
AWS S3
MaxCDN



Javascript with Types
Developed by Microsoft
Apache Licensed
Works on Mac OS X and Linux using Node

Why TypeScript?

Type Safety
Type Definitions for Javascript
IDE Support
Smart Code Completion
Refactoring
Some of ES6 Now


Some of ES6 Now

Arrow functions
(name) => "Hello " + name;
Default Arguments
function hello(name="World") { return "Hello " + name; } 
Classes
class Greeter { constructor(name) { this.name = name; } } 
Modules
module greeters { /* encapsulated code */ } 

Type Safety and Inference


var helloString = "Hello World"!;
var helloArray = ["Hello World!"];

function helloFunction(name) { return "Hello " + name + "!"; }

Type Safety and Inference


var helloString : string = "Hello World!";
var helloArray : string[] = ["Hello World!"];

function helloFunction(name : string) : string { return "Hello " + name + "!"; }

Compiled to Javascript


var helloString = "Hello World!";

var helloArray = ["Hello World!"];

function helloFunction(name) {
    return "Hello " + name + "!";
}
//# sourceMappingURL=test.js.map

IDE Support


Visual Studio

Web Storm and IntelliJ
 
Plugins for vim, Sublime Text, emacs



Writing an AngularJS App in TypeScript


References

/// <reference path="types/angularjs/angular.d.ts"/>
/// <reference path="types/angularjs/angular-route.d.ts"/>
/// <reference path="types/restangular/restangular.d.ts"/>

Type definitions make Javascript type safe

interface IRouteProvider extends IServiceProvider {
    otherwise(params: any): IRouteProvider;
    when(path: string, route: IRoute): IRouteProvider;
}
interface IRoute {
    controller?: any;
    controllerAs?: any;
    name?: string;
    template?: string;
    templateUrl?: any;
    resolve?: any;
    redirectTo?: any;
    reloadOnSearch?: boolean;
}    

Routing

angular.module("myApp", ["ngRoute", "restangular"])
    .config(["$routeProvider",
        ($routeProvider: ng.route.IRouteProvider) => {
            $routeProvider
                .when("/", {
                    templateUrl: "views/main.html",
                    controller: "MainCtrl",
                    resolve: {
                        entries: ["Restangular", (Restangular: Restangular) => {
                            return Restangular.all("entries").getList();
                        }]
                    }
                })
                .otherwise("/")
    }]);

Controllers

class MainCtrl {

    constructor(private $scope, public entries: ArrayRest<IEntryRest>) {
        $scope.vm = this;
    }
    
    // ...

    public upvote(entry: IEntryRest) {
        // POST on /entries/:entryId/upvote
        entry.post("upvote", {}).then((updatedEntry: IEntryRest) => {
            entry.votes = updatedEntry.votes;
        });
    }
}

angular.module("myApp") .controller('MainCtrl', ['$scope', 'entries', MainCtrl]);


Building and Deploying using Grunt 



Grunt tasks

Grunt is a Javascript 'Task' runner
Tasks are defined in the Gruntfile.js
The Gruntfile.js is just a node.js script

$ grunt test$ grunt server$ grunt build$ grunt deploy

Run the tests


Unit testing with Karma and Phantom JS

End to end tests with Selenium



Configuration

Config for development, QA and production

Available as an AngularJS service called 'config'

Grunt inserts the correct environment config into the service when we build

var config: Config;
//@@config;
angular.module('labs.common.services').constant('config', config);

Building the app

  • Compile the TypeScript files and embeds the settings file
  • Compile the less files
  • Concat everything within blocks in the index.html file
<!-- build:js({.tmp,app}) scripts/scripts.js -->
<script src="scripts/app.js"></script>
<script src="scripts/controllers/controllerA.js"></script> 
<script src="scripts/services/serviceB.js"></script>
<!-- endbuild -->
  • Minify the resulting css and js files
  • Append the hash of the file to the name as a cache buster   
  • Replace libraries with a CDN version
  • Put everything in a folder ready for deployment

Deploying to S3


Deploy into S3 folder using grunt-aws-s3

 var folder=timestamp + "-" + git_commit_hash 

e.g. 201401091540-fc90307

Set expiry headers for index.html

params: { Cache-Control: max-age=300 }

Update CDN


We wrote our own grunt-netdna library

Updates the pull zone to use the new S3 URL

Calls purge on the pull zone to clear out data



A few more things

Definitely Typed


If you need to depend on the latest version append the git commit hash to the url to avoid breaking future builds

Setting up the Gruntfile


Setting up the Gruntfile can take time but it's well worth it as it can automate a lot of your workflow

NPM shrinkwrap



Use npm shrinkwrap to manage your dependencies
> npm shrinkwrap

NPM registry


Switch to the EU npmjs registry mirror

npm --registry http://registry.npmjs.eu/ install

npm set registry http://registry.npmjs.eu/ 



Thank you

@Curial
@foldr

typed-angular

By jamiemccrindle