Andrew Fontaine

Mostly code

Angular, TypeScript, RequireJS, and More, Pt. 2

10 Jan 2016

This is part 2 of our series. Click here if you need to read part 1.

Now that we have our project set up, we can start building our application.

This post contains a number of templates you can use to start hacking away.

Module

/* To ensure other modules your app depends on loads before your app,
 * ensure you have these amd-dependecy special comments at the top.
 * This loads them without actually importing them.
 *
 * Be sure to export your module, so you can register components on it
 * later.
 */

/// <amd-dependency path="ui-router" />
/// <amd-dependency path="ui-bootstrap" />
import {module} from "angular";

export var app: angular.IModule = module("app", [
  "ui-router",
  "ui-bootstrap"
]);

Controller

/* We can use classes as controllers. These classes can also work as
 * controllers for directives.
 */

import {controller, IHttpService, IScope} from "angular";
import {app} from "../app.module";

// If we want things on the $scope, we need to make a custom scope interface
// that extends the default.
IAppScope extends IScope {
  disabled: boolean
}

// export the class so we can import it into our unit tests
export class AppController {

  // To inject dependencies, use the $inject syntax
  public static $inject: Array<string> ["$scope", "$http"];

  // Put whatever you're binding on your views in the public space
  public user: string;

  // Be sure to save references to injected services if you need access to them
  // outside the constructor
  private $http: IHttpService;
  private $scope; IAppScope;

  constructor($scope: IAppScope, $http: IHttpService) {
    $scope.disabled = true;
    this.$scope = $scope;
    this.$http = $http;
    $http.get("http://example.com/myresource").then((data: string) => {
      this.user = data;
      $scope.disabled = true;
    });
  }
}

app.controller("AppController", AppController);

Directive

import {IDirective} from "angular";
import {app} from "../app.module";

app.directive("myDirective", MyDirective.Factory);

// Now we can even extend our directive if we want to inherit from it.
class MyDirective {
  public restrict: string = "E";
  public controller: MyDirectiveController = MyDirectiveController;
  public controllerAs: string = "vm";

  public static Factory(): IDirective {
    return new this();
  }
}

export class MyDirectiveController {
  // ...
}

Service

import {IHttpService} from "angular";
import {app} from "../app.module";

// Make sure to make interfaces of any models you're getting.
export interface IJsonPlaceHolderPost {
  userId: number;
  title: string;
  body: string;
}

// Much like classes, services are new'd up, so we can use regular classes
// Export the service so we can import it into our unit tests later.
export class MyService {

  public static $inject = ["$http"];

  private $http: angular.IHttpService

  constructor($http: angular.IHttpService) {
    this.$http = $http;
  }

  public getPost(id: number): angular.IHttpPromise<IJsonPlaceholderPost> {
    return this.$http.get(`http://jsonplaceholder.typicode.com/posts/${id}`);
  }
}

app.service("myService", MyService);

Factory

import {IWindowService} from "angular";
import {app} from "../app.module";

export interface IUserInfo {
  username: string;
  token: string;
  fullName: string;
}

// This time we use a factory pattern to create the service.
export class MyService {

  public userInfo: IUserInfo;

  constructor(userInfo: IUserInfo) {
    this.userInfo = userInfo;
  }

  public static factory($window: IWindowService) {
    var userInfo: IUserInfo = JSON.parse<IUserInfo>($window.localStorage.userInfo);
    return new this(userInfo);
  }
}

MyService.factory.$inject = ["$window"];

app.factory("myService", MyService.factory);

Provider

The biggest difference here is that we can configure a provider during any angular config calls, such as injecting an API endpoint.

import {IHttpService, IServiceProvider} from "angular";
import {app} from "../app.module";

export interface IUserInfo {
  username: string;
  token: string;
  fullName: string;
}

export class MyServiceProvider extends IServiceProvider {
  public static endpoint: string;

  constructor() {
    this.$get.$inject = ["$http"];
  }

  public $get($http: IHttpService): MyService {
    return new MyService($http, MyServiceProvider.endpoint);
  }
}

export class MyService {
  private $http: IHttpService;
  private endpoint: string;

  constructor($http: IHttpService, endpoint: string) {
    this.$http = $http;
    this.endpoint = endpoint;
  }

  public getUserInfo(user: string) {
    this.$http.get(`${this.endpoint}/users/${user}`);
  }
}

angular.provider("MyService", MyServiceProvider);

I ❤ feedback. Let me know what you think of this article on Twitter @afontaine_ca