@drpicox
HOMEBLOGTESTINGTEACHING

AngularJS components in Typescript

Shows how to write Angular1 components using the .component syntax of AngularJS, and Typescript using classes and DDO.

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

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

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;

More information

Copyright © 2022 David Rodenas
G · T · M π