Angular 2

Services + DI

Data Model

  • In Angular 2 (similar to 1.x), data model is separated from the view.
  • Data model is implemented as Services which are actually injectable classes
  • Angular 2 uses a Dependency Injection mechanism to instansitate and inject Services

Dependency Injection & Provider

  • The Angular 2 DI mechanism includes the following:
    • Injector - The injector object that exposes APIs to us to create instances of dependencies.
    • Provider - A provider is like a recipe that tells the injector how to create an instance of a dependency. A provider takes a token and maps that to a factory function that creates an object.
    • Dependency - A dependency is the type of which an object should be created.

Service

  • In Angular 2, a Service is simply a class that can be injected.
  • @Injectable decorator is used to create an injectable service
import (Injectable) from 'angular2/core';

@Injectable()
export class DataService {
}
  • To use the service we need to tell the DI mechanism about the injectable object and its type

Depenency Injection Provider

  • Telling the DI mechanism how to instanciate an inject is done by a Provider (a provider is like a recipe that tells the injector how to create an instance of a dependency)
  • Injector services are singletons (created once)
     
  • Angualr 2 dependency injection support differnt types of providers: ClassesFactories and Values
  • Angular 2 includes a provider API:
    • provide(<provider token>, { useClass<class name> })
    • provide(<provider token>, { useFactory<factory name> })
    • provide(<provider token>, { useValue<value name> })
  • The provider token is the unique identifier for the Injector. It can be a string or better an OpaqueToken object

Depenency Injection Provider

  • Providers can be created as needed either at Component level or bootstrap level
  • At Component level, the decorator includes a providers list that can include needed providers
  • For classes, instead of the long syntax for creating a provider we can just use the Injectable class type
import {Component, provide} from 'angular2/core';
import {DataService} from './data.service';

@Component({
    selector: 'my-component',
    template: `<div>My Component</div>`,
    // providers: [provide(DataService, {useClass: DataService}]
    providers: [DataService]
})
class MyComponent {
    constructor() {}
}

Service

  • Once a provider is available, we can use Inject decorator to resolve and create the class instance (as described for the provider)
import {Component, Inject} from 'angular2/core';
import {DataService} from './data-service';

@Component({
    selector: 'my-component',
    template: `<div>My Component</div>`,
    providers: [DataService]
})
class MyComponent {
   
    constructor(@Inject(DataService) dataService : DataService) {
        console.log(this.dataService.data);
    }
}

Service

  • TypeScript allows a shorter syntax instead of using @Inject
import {Component, Inject} from 'angular2/core';
import {DataService} from './data-service';

@Component({
    selector: 'my-component',
    template: `<div>My Component</div>`,
    providers: [DataService]
})
class MyComponent {
   
    constructor(private dataService : DataService) {
        console.log(this.dataService.data);
    }
}

Dependecy Injection Provider

  • We can use providers not only for class but for other types for example values
import {Component, Inject, provide, OpaqueToken} from 'angular2/core';
import {DataService} from './data-service';

const LoginToken = new OpaqueToken('LoginServiceConfig');

@Component({
    selector: 'my-component',
    template: `<div>My Component</div>`,
    providers: [DataService,
                provide(LoginToken, { useValue: { url: 'myurl' } })]
})
class MyComponent {
   
    constructor(private dataService : DataService,
                @Inject(LoginToken) myLoginServiceConfig) {
        console.log(this.dataService.data);
        console.log(this.myLoginServiceConfig.url);
    }
}

Dependecy Injection Provider

  • In Angular 2, there is no single injector. We can also have child injectors
  • Child injectors can also have child injectors resulting in a hierarchy of injectors
  • Each injector is a stand-alone injector that can have providers to resolve and create instances

Dependecy Injection Provider

  • Child injectors are special in a way that if a dependency is not resolved, it will keep looking at the parent injector
  • The top most injector is the root injector. Providers for it can be defined during application bootstrap
  • In Angular 2, root , partent and child injectors are created automatically when we define providers at any level
    • Even if not completely true, it is useful to pretend that every component has its own injector.

Dependecy Injection Provider

  • Example
  • If we want to have a Singelton Service for all Components it should be defined at Root Injector

Dependecy Injection Provider

  • Adding providers for root injector at bootstrap can be done as additional parameter to bootstrap
import {bootstrap, provide} from 'angular2/platform/browser';

import {MyComponent} from './my-component.component';
import {DataService} from './data-service';

const LoginToken = new OpaqueToken('LoginServiceConfig');

bootstrap(MyComponent, [
         DataService, // same as provide(DataService, {useClass: DataService})
         provide(LoginToken, { useValue: { url: 'myurl' }})
         
     ])  // Additional parameter is an array of providers
    .catch( err => console.log(err));

Dependecy Injection Provider

  • The major use for providers is for data model Services
  • We can also use value providers for other types of Angular objects like Directives, PIPES, Validators, etc.
  • Using value providers for other types enable us to take advantage of the DI mechanism for other types and not only for Services / Constants
@Directive(...)
class Draggable { }

@Directive(...)
class Morphable { }

@Component(...)
class RootCmp { }

// at bootstrap
bootstrap(RooCmp, [
  provide(DRAG_DIRECTIVES, {useValue: Draggable }),
  provide(MORPH_DIRECTIVES, {useValue: Morphable })
]);

Dependecy Injection Provider

  • Angular 2 also comes with bulitin providers that we can use:
    • ENV_PROVIDERS, HTTP_PROVIDER, ROUTER_PROVIDERS, etc.
import {bootstrap, provide} from 'angular2/platform/browser';

import {MyComponent} from './my-component.component';
import {DataService} from './data-service';

const LoginToken = new OpaqueToken('LoginServiceConfig');

bootstrap(MyComponent, [
         ...ENV_PROVIDERS,
         ...HTTP_PROVIDERS,
         ...ROUTER_PROVIDERS,
         DataService, // same as provide(DataService, {useClass: DataService})
         provide(LoginToken, { useValue: { url: 'myurl' }})
         
     ])  // Additional parameter is an array of providers
    .catch( err => console.log(err));
  • The builtin providers are arrays. The '...' notation is a short syntax for breaking array to elements.

Dependecy Injection Provider

  • Angular 2 also supports multi providers which means we can provide multiple values to a single token
  • Angular 2 includes builtin multi providers called PLATFORM_PROVIDERS that are automatically added for root injector
  • These providers can be used to add custom providers, (Services, Direictives, validations, pipes, etc.) to Angular's global ones
  • This can be done using the "multi" parameter of the provide function. This parameter is mandatory when using multi providers (otherwise it will not work properly)
bootstrap(RooCmp, [
  provide(PLATFORM_DIRECTIVES, {useValue: Draggable, multi: true}),
  provide(PLATFORM_DIRECTIVES, {useValue: Morphable, multi: true})
]);

Dependecy Injection Provider

  • Angular 2 several builtin multi providers
    • PLATFORM_DIRECTIVES - Used for Directives
    • PLATFORM_PIPES - Basically same as PLATFORM_DIRECTIVES just for pipes
    • NG_VALIDATORS - Interface that can be implemented by classes that can act as validators
    • PLATFORM_INITIALIZER - Can be used to perform initialization work
    • EVENT_MANAGER_PLUGINS - Plugins to extend the event system with custom events and behavior (this might be refactored with a different system soon)

Dependecy Injection Provider

  • It is possible to create our own lists of providers, directives, either using PLATFORM or just for packaing serveral providers together
export const DIRECTIVES = [
  provide(PLATFORM_DIRECTIVES, {useValue: APPLICATION_DIRECTIVES, multi: true})
];

Dependecy Injection Provider

  • In Angular 2, we can also access to an Injector and use its API by importing the Injector module. This can be useful for testing
  • Injector has API to resolve and create instances:
    • resolveAndCreate()
    • resolveAndCreateChild()
    • get
    • ...
       
  • Additional advanced options (TBD):
    • @Host, @Optional, UseExisting

Angular 2 Services + DI

By risweb

Angular 2 Services + DI

  • 610