Angular 2 — An introduction of bootstrap and providers


Angular 2 does not have a bootstrap directive (ng-app). We always launch the app in code by explicitly calling a bootstrap function and passing it the name of the application's module (AppComponent).

Angular 2 standard module bootstrap is loaded from the angular2/platform/browser module. Using this module we have access to the bootstrap method, which we use right after the import statements to start our Angular 2 application by loading the App component via this method.

To load our main component and pass AppComponent as a parameter to the bootstrap method, we have to make sure that the component is imported first. By calling the bootstrap the framework starts loading the main component into the component tree. Child components are loaded next.

Let’s have a look to the bootstrap definition….

export bootstrap(
appComponentType: Type,
customProviders?: Array<any /*Type | Provider | any[]*/>
) : Promise<ComponentRef>

Hmmmm…The first parameter above the bootstrap definition ‘appComponentType’ we have already discussed and the second parameter where it says ‘customProviders’ is optional; it is an array of what we want to make globally available for injection using dependency injection. But…what does dependency injection mean? What are providers? How many types of providers do we have? How do we use them?

Let’s start with some theory…

What is dependency injection?

In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it.

Wikipedia

What is a provider?

A provider describes what the injector should instantiate a given token, so it describes how an object for a certain token is created.

Provider definition…
export provide(token: any, {useClass, useValue, useExisting, useFactory, deps, multi}: {
useClass?: Type,
useValue?: any,
useExisting?: any,
useFactory?: Function,
deps?: Object[],
multi?: boolean
}) : Provider

How many types of providers do we have?

Angular 2 offers us the following type of providers:

  • Class Provider (useClass)
  • FactoryProvider (useFactory)
  • Aliased Class Provider (useExisting)
  • Value Provider (useValue)

How do we use them?

Let’s start from the beginning…

Imagine we have a component that is depending on a service which gets a list of all cars from a database. We just want to consume that service from our component; so our service would be something like this:

import {Injectable} from 'angular2/core';
@Injectable()
export class CarService {
getCars = () => [
{ id: 1, name: 'BMW' },
{ id: 2, name: 'Suzuki' },
{ id: 3, name: 'Volkswagen' }
];
}
  • @Injectable : is a decorator, that informs Angular 2 that the service has some dependencies itself. Basically services in Angular 2 are simple classes with the decorator @Injectable on top of the class, that provides a method to return some items.

…and in our component where we are going to consume the service:

import {CarService} from './carService';
@Component({
selector: 'list-cars',
template: `
<ul>
<li *ngFor="#car of cars">{{car.name}}</li>
</ul>
`
})
class AppComponent {
cars:Array<any>;
constructor(private _carService: CarService) {
this.cars = _carService.getCars();
}
}

As you can see above, we are passing from our constructor the CarService class we are asking for and Angular 2 knows how to create and inject an object of type CarService, if we configure a provider for it.

Class Provider (useClass)

This can be achieved either in the bootstrapping process of our app, or in the component itself (both ways have different implications on the dependency’s life cycle and availability). If you use the bootstrap process the service will be available in the entire application whilst using the component you will have it encapsulated.

Using the bootstrap process:
bootstrap(AppComponent, [ 
provide(CarService, {useClass: CarService})
]);
Using the component process:
@Component({  

providers: [
provide(CarService, {useClass: CarService})
]

})
export class AppComponent { }

We are telling the Injector that we want to create a link between a token (the type CarService) and the class CarService, using the provider method.

The Injector is a service that keeps track of the injectable components by maintaining a registry and injects them when needed.

The registry is a map that associates keys (called tokens) with classes. The tokens are not necessarily string, they can be anything, like Type references, for example. And that will usually be the case.

There’s a shorthand syntax we can use if the instruction is useClass and the value of it the same as the token, which is the case in this particular provider:

// bootstrap 
bootstrap(AppComponent, [CarService]);
// component 
@Component({

providers: [CarService]
})
class AppComponent { }

Whenever you ask for a dependency of type CarService, Angular 2 knows how to create an object for it.

What if we need to use our CarService to fetch the data from a remote server using Http?

Let’s change our code a little bit…

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
@Injectable()
export class CarService {
constructor(private _http : Http){}
getCars = () => this._http.get('http://localhost:8000/cars')
.map(res => res.json());
}

Here we are using the Http service that is provided by Angular 2 and we still need to “register” the Http service to make it available for injection. An easy way to do this is declare it from the bootstrap method:

import {HTTP_PROVIDERS} from 'angular2/http'
// ...
bootstrap(AppComponent, [HTTP_PROVIDERS]);

If we want to make our CarService available for injection in other services or components we have to register it too:

import {HTTP_PROVIDERS} from 'angular2/http'
import {CarService} from './carService';
// ...
bootstrap(AppComponent, [HTTP_PROVIDERS, CarService]);

…and we can use our service whenever we want!

FactoryProvider (useFactory)
import {HTTP_PROVIDERS} from 'angular2/http'
// ...
const IS_PROD = false;
bootstrap(AppComponent, [
HTTP_PROVIDERS,
//we provide a factory
provide(CarService, {
useFactory: () => IS_PROD ?
new FakeCarService() : new CarService()
})
]);

We are using useFactory instad of useClass. A factory is a function that creates an instance and in our example above it is returning the fake service or the real service depending on the value of the IS_PROD constant. The problem is when you try to create the instance of the CarService; we do not have the Http dependencies instantiated, so the useFactory can be used with another property named deps, where you can specify an array of dependencies:

import {HTTP_PROVIDERS} from 'angular2/http'
// ...
const IS_PROD = true;
bootstrap(AppComponent, [
HTTP_PROVIDERS,
//we provide a factory
provide(CarService, {
useFactory: http => IS_PROD ?
new FakeCarService() : new CarService(http),
deps: [Http]
})
]);
Value Provider (useValue)

We can use dependency injection too for the IS_PROD constant, using another provider type: useValue, but we do not need to push everything to DI; it is only to show another type of provider.

import {HTTP_PROVIDERS, Http} from 'angular2/http'
// ...
const IS_PROD = true;
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide('IS_PROD', {useValue: true}),
provide(CarService, {
useFactory: (IS_PROD, http) => IS_PROD ?
new FakeCarService() : new CarService(http),
deps: ['IS_PROD', Http]
})
]);
Aliased Class Provider (useExisting)

How can we declare two providers for the same class with two different tokens? That’s where useExisting comes into play:

import {HTTP_PROVIDERS} from 'angular2/http'
// ...
const IS_PROD = true;
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide('CarServiceToken', {useClass: CarService}),
provide(CarService, {useExisting: 'CarServiceToken'})
]);

…not something you will need every day!

Binding multiple values

We can have only one value per token; if we define another one, it will replace the first binding but what would happen if we want to declare a collection of values and be able to add an element to the collection?

This is very useful if we want to add a custom pipe, or a custom directive or a custom validator to the default collections and if we want to have it available globally in the application.

To achieve this, we have an option called: multi which is false by default but, if a binding is declared using multi:true, then it is possible to add other values to this binding later.

Example:

bootstrap(AppComponent, [
provide('languages', {useValue: 'en', multi:true }),
provide('languages', {useValue: 'es', multi:true })
]).then(
//play with the returned injector
appRef => playWithBootstrap(appRef.injector)
);

And, because the bootstrap is a promise, we can play with it using the function playWithBootstrap

function playWithBootstrap(inj) {
console.log(inj.get('languages'));
//logs "['en','es']"
}