close

  menu      DavidRodenas
CONTACT  
Main article: Teaching »

Writing Angular1 components in Typescript (ddo form)

September 09, 2016

Shows how to write Angular1 components using the new .component syntax of Angular 1.5, and typescript using classes and DDO. It is closer to Angular1 intentions and do not create a new way to do the same.

Tags: teaching, angularjs, typescript


export const HelloWorldComponent = {
  template: `Hello {{ "{{$ctrl.name"}}}}`,
  controller: class HelloWorldController {
    constructor() {
      this.name = 'World';
    }
  }
}

Yesterday I have presented a mechanism to use Typescript (or ES2015) and just classes to create components.

After a little talk with Angular1 core team they have proposed the alternate to use just plain DDOs and avoid creating new ways of doing the same thing.

Here I present how to use DDOs and Typescript (or ES2015) as an alternate version.

Implementation overview

// counter.component.ts
export const CounterComponent = {
  bindings: {
    initialCount: '<',
  };
  template: `
    <p>
      <button ng-click="$ctrl.increment()">+</button>
      <button ng-click="$ctrl.decrement()">-</button>
      Count: {{$ctrl.count}}
    </p>
  `;
  controller: class CounterController {
    initialCount: number;  
    salute: string;
    
    /* @ngInject */
    constructor() {
    }
    
    $onInit() {
      this.count = this.initialCount;
    }
    
    increment() {
      this.count++;
    }
    
    decrement() {
      this.count--;
    }
  }
}
// app.module.ts
import { CounterComponent } from './counter.component';

export const AppModule = angular
  .module('AppModule', [])
  .component('myCounter', CounterComponent)
  .name;

Step by step explanation

Component declaration and registration

Create the component as a constant DDO object and register it.

// counter.component.ts
export const CounterComponent = {
  // ...
}

Import the component in the module and register it.

// app.module.ts
import { CounterComponent } from './counter.component';

export const AppModule = angular
  .module('AppModule', [])
  .component('myCounter', CounterComponent)
  .name;

Component definition

Define component configuration as properties of the DDO object.

export class CounterComponent {
  bindings: { /* ... */ },
  template: `...`,
  // ...
}

If you need a controller, define it inside the DDO:

export class CounterComponent {
  // ...
  controller: class CounterController {
    // ...
  }
}

Component controller properties

Add as instance properties bindings and other controller state properties.

  controller: class CounterController {
    initialCount: number;  
    salute: string;
    // ...
  }

Component controller constructor

The constructor for the component. Usually it defines injections, add `/* @ngInject */ comment so ngannotate can do its work.

  controller: class CounterController {
    // ...
    /* @ngInject */
    constructor() {
    }
  }

Component controller methods

Define all your component logic as methods.

  controller: class CounterController {
    // ...
    increment() {
      this.count++;
    }
    
    decrement() {
      this.count--;
    }
  }

Component hooks

Declare you component hooks as methods of the class.

  controller: class CounterController {
    // ...
    $onInit() {
      this.count = this.initialCount;
    }
  }

More information about lifecycle hooks here.

How to make injections

You can leverage in Typescript to have injections with type checking and auto-completion support.

// ./hello-world.component
import { SaluteService } from './salute.service';

export const HelloWorldComponent = {
  bindings: {
    name: '<',
  },
  template: `
    <h1>{{$ctrl.salute}}</h1>
  `,
  controller: class HelloWorldController {
    name: string;
    salute: string;
    
    /* @ngInject */
    constructor(private saluteService: SaluteService) {
    }
    
    $onChanges(changes) {
      if (changes.name && changes.name.currentValue) {
        this.salute = this.saluteService.salute(this.name);
      }
    }
  }
}

Import the service class at it should be done in Angular2, and inject it in the constructor. If you need the injection elsewhere than constructor, safe it in a field of the class.

Make sure that the argument name the injected service matches exactly with the string defined in the module.

import { SaluteService } from './salute.service';
import { HelloWorldComponent } from './hello-world.component';

export const AppModule = angular
  .module('AppModule', [])
  .service('saluteService', SaluteService)
  .component('myHelloWorld', HelloWorldComponent)
  .name;

ES2015

Everything is supported in ES2015 but types and class properties. Instance properties are initialized inside the constructor if required and types are removed.

This is the same code with ES2015:

// counter.component.js
export const HelloWorldComponent = {
  bindings: {
    name: '<',
  },
  template: `
    <h1>{{$ctrl.salute}}</h1>
  `,
  controller: class HelloWorldController {
    /* @ngInject */
    constructor() {
    }
    
    $onInit() {
      this.count = this.initialCount;
    }
    
    increment() {
      this.count++;
    }
    
    decrement() {
      this.count--;
    }
  }
}
// app.module.js
import { CounterComponent } from './counter.component';

export const AppModule = angular
  .module('AppModule', [])
  .component('myCounter', CounterComponent)
  .name;

More information

  • See here for a live demo: http://plnkr.co/edit/1LXJjc?p=preview
  • See https://github.com/drpicox/david-rodenas.com/tree/v5.5.0 for a real example

« Back to List