You are on page 1of 47

11/20/2016 Dependency 

Injection ­ ts ­ COOKBOOK

DEPENDENCY INJECTION

Techniques for Dependency Injection

Dependency Injection is a powerful pattern for managing code dependencies. In this


cookbook we will explore many of the features of Dependency Injection (DI) in
Angular.

Table of contents
Application-wide dependencies

External module con嘀guration

@Injectable and nested service dependencies

Limit service scope to a component subtree

Multiple service instances (sandboxing)

Qualify dependency lookup with @Optional and @Host

Inject the component's DOM element

De嘀ne dependencies with providers

The provide object literal


https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 1/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

useValue - the value provider


useClass - the class provider
useExisting - the alias provider
useFactory - the factory provider

Provider token alternatives

class-interface
OpaqueToken

Inject into a derived class

Find a parent component by injection

Find parent with a known component type


Cannot 嘀nd a parent by its base class
Find a parent by its class-interface
Find a parent in a tree of parents (@SkipSelf)
A provideParent helper function

Break circularities with a forward class reference (forwardRef)

See the live example of the code supporting this cookbook.

Application-wide dependencies
Register providers for dependencies used throughout the application in the root
application component, AppComponent .

In the following example, we import and register several services (the


LoggerService , UserContext , and the UserService ) in the @Component

metadata providers array.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 2/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

app/app.component.ts (excerpt)

import { LoggerService } from './logger.service';


import { UserContextService } from './user-context.service';
import { UserService } from './user.service';

@Component({
moduleId: module.id,
selector: 'my-app',
templateUrl: 'app.component.html',
providers: [ LoggerService, UserContextService, UserService ]
})
export class AppComponent {
/* . . . */
}

All of these services are implemented as classes. Service classes can act as their
own providers which is why listing them in the providers array is all the
registration we need.

A provider is something that can create or deliver a service. Angular


creates a service instance from a class provider by "new-ing" it. Learn
more about providers below.

Now that we've registered these services, Angular can inject them into the
constructor of any component or service, anywhere in the application.

app/hero-bios.component.ts (component constructor injection)

constructor(logger: LoggerService) {
logger.logInfo('Creating HeroBiosComponent');

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 3/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

app/user-context.service.ts (service constructor injection)

constructor(private userService: UserService, private


loggerService: LoggerService) {
}

External module con嘀guration


We often register providers in the NgModule rather than in the root application
component.

We do this when (a) we expect the service to be injectable everywhere or (b) we


must con嘀gure another application global service before it starts.

We see an example of the second case here, where we con嘀gure the Component
Router with a non-default location strategy by listing its provider in the providers
list of the AppModule .

app/app.module.ts (providers)

providers: [
{ provide: LocationStrategy, useClass: HashLocationStrategy }
]

@Injectable and nested service dependencies


https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 4/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

The consumer of an injected service does not know how to create that service. It
shouldn't care. It's the dependency injection's job to create and cache that service.

Sometimes a service depends on other services ... which may depend on yet other
services. Resolving these nested dependencies in the correct order is also the
framework's job. At each step, the consumer of dependencies simply declares what
it requires in its constructor and the framework takes over.

For example, we inject both the LoggerService and the UserContext in the
AppComponent .

app/app.component.ts

constructor(logger: LoggerService, public userContext:


UserContextService) {
userContext.loadUser(this.userId);
logger.logInfo('AppComponent initialized');
}

The UserContext in turn has dependencies on both the LoggerService (again)


and a UserService that gathers information about a particular user.

user-context.service.ts (injection)

@Injectable()
export class UserContextService {
constructor(private userService: UserService, private
loggerService: LoggerService) {
}
}

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 5/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

When Angular creates an AppComponent , the dependency injection framework


creates an instance of the LoggerService and starts to create the
UserContextService . The UserContextService needs the LoggerService ,

which the framework already has, and the UserService , which it has yet to create.
The UserService has no dependencies so the dependency injection framework
can just new one into existence.

The beauty of dependency injection is that the author of AppComponent didn't care
about any of this. The author simply declared what was needed in the constructor
( LoggerService and UserContextService ) and the framework did the rest.

Once all the dependencies are in place, the AppComponent displays the user
information:

@Injectable()
Notice the @Injectable() decorator on the UserContextService class.

user-context.service.ts (@Injectable)

@Injectable()
export class UserContextService {
}

That decorator makes it possible for Angular to identify the types of its two

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 6/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

dependencies, LoggerService and UserService .

Technically, the @Injectable() decorator is only required for a service class that
has its own dependencies. The LoggerService doesn't depend on anything. The
logger would work if we omitted @Injectable() and the generated code would be
slightly smaller.

But the service would break the moment we gave it a dependency and we'd have to
go back and and add @Injectable() to 嘀x it. We add @Injectable() from the
start for the sake of consistency and to avoid future pain.

Although we recommend applying @Injectable to all service classes, do not


feel bound by it. Some developers prefer to add it only where needed and that's
a reasonable policy too.

The AppComponent class had two dependencies as well but no


@Injectable() . It didn't need @Injectable() because that

component class has the @Component decorator. In Angular with


TypeScript, a single decorator — any decorator — is suf嘀cient to identify
dependency types.

Limit service scope to a component subtree


All injected service dependencies are singletons meaning that, for a given
dependency injector ("injector"), there is only one instance of service.

But an Angular application has multiple dependency injectors, arranged in a tree


hierarchy that parallels the component tree. So a particular service can be provided
https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 7/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

(and created) at any component level and multiple times if provided in multiple
components.

By default, a service dependency provided in one component is visible to all of its


child components and Angular injects the same service instance into all child
components that ask for that service.

Accordingly, dependencies provided in the root AppComponent can be injected into


any component anywhere in the application.

That isn't always desirable. Sometimes we want to restrict service availability to a


particular region of the application.

We can limit the scope of an injected service to a branch of the application hierarchy
by providing that service at the sub-root component for that branch. Here we provide
the HeroService to the HeroesBaseComponent by listing it in the providers
array:

app/sorted-heroes.component.ts (HeroesBaseComponent excerpt)

1. @Component({
2. selector: 'unsorted-heroes',
3. template: `<div *ngFor="let hero of heroes">{{hero.name}}</div>`,
4. providers: [HeroService]
5. })
6. export class HeroesBaseComponent implements OnInit {
7. constructor(private heroService: HeroService) { }
8. }

When Angular creates the HeroesBaseComponent , it also creates a new instance


of HeroService that is visible only to the component and its children (if any).

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 8/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

We could also provide the HeroService to a different component elsewhere in the


application. That would result in a different instance of the service, living in a
different injector.

We examples of such scoped HeroService singletons appear


throughout the accompanying sample code, including the
HeroBiosComponent , HeroOfTheMonthComponent , and

HeroesBaseComponent . Each of these components has its own

HeroService instance managing its own independent collection of

heroes.

Take a break!
This much Dependency Injection knowledge may be all that many Angular
developers ever need to build their applications. It doesn't always have to be
more complicated.

Multiple service instances (sandboxing)


Sometimes we want multiple instances of a service at the same level of the
component hierarchy.

A good example is a service that holds state for its companion component instance.
We need a separate instance of the service for each component. Each service has
its own work-state, isolated from the service-and-state of a different component. We

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 9/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

call this sandboxing because each service and component instance has its own
sandbox to play in.

Imagine a HeroBiosComponent that presents three instances of the


HeroBioComponent .

ap/hero-bios.component.ts

1. @Component({
2. selector: 'hero-bios',
3. template: `
4. <hero-bio [heroId]="1"></hero-bio>
5. <hero-bio [heroId]="2"></hero-bio>
6. <hero-bio [heroId]="3"></hero-bio>`,
7. providers: [HeroService]
8. })
9. export class HeroBiosComponent {
10. }

Each HeroBioComponent can edit a single hero's biography. A


HeroBioComponent relies on a HeroCacheService to fetch, cache, and perform

other persistence operations on that hero.

app/hero-cache.service.ts

1. @Injectable()
2. export class HeroCacheService {
3. hero: Hero;
4. constructor(private heroService: HeroService) {}
5.

6. fetchCachedHero(id: number) {
7. if (!this.hero) {
8. this.hero = this.heroService.getHeroById(id);

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 10/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

9. }
10. return this.hero;
11. }
12. }

Clearly the three instances of the HeroBioComponent can't share the same
HeroCacheService . They'd be competing with each other to determine which hero

to cache.

Each HeroBioComponent gets its own HeroCacheService instance by listing the


HeroCacheService in its metadata providers array.

app/hero-bio.component.ts

1. @Component({
2. selector: 'hero-bio',
3. template: `
4. <h4>{{hero.name}}</h4>
5. <ng-content></ng-content>
6. <textarea cols="25" [(ngModel)]="hero.description">
</textarea>`,
7. providers: [HeroCacheService]
8. })
9.

10. export class HeroBioComponent implements OnInit {


11. @Input() heroId: number;
12.

13. constructor(private heroCache: HeroCacheService) { }


14.

15. ngOnInit() { this.heroCache.fetchCachedHero(this.heroId); }


16.

17. get hero() { return this.heroCache.hero; }


18. }

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 11/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

The parent HeroBiosComponent binds a value to the heroId . The ngOnInit


pass that id to the service which fetches and caches the hero. The getter for the
hero property pulls the cached hero from the service. And the template displays

this data-bound property.

Find this example in live code and con嘀rm that the three HeroBioComponent
instances have their own cached hero data.

Qualify dependency lookup with @Optional and @Host


We learned that dependencies can be registered at any level in the component
hierarchy.

When a component requests a dependency, Angular starts with that component's


injector and walks up the injector tree until it 嘀nds the 嘀rst suitable provider. Angular
throws an error if it can't 嘀nd the dependency during that walk.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 12/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

We want this behavior most of the time. But sometimes we need to limit the search
and/or accommodate a missing dependency. We can modify Angular's search
behavior with the @Host and @Optional qualifying decorators, used individually
or together.

The @Optional decorator tells Angular to continue when it can't 嘀nd the
dependency. Angular sets the injection parameter to null instead.

The @Host decorator stops the upward search at the host component.

The host component is typically the component requesting the dependency. But
when this component is projected into a parent component, that parent component
becomes the host. We look at this second, more interesting case in our next
example.

Demonstration
The HeroBiosAndContactsComponent is a revision of the HeroBiosComponent
that we looked at above.

app/hero-bios.component.ts (HeroBiosAndContactsComponent)

1. @Component({
2. selector: 'hero-bios-and-contacts',
3. template: `
4. <hero-bio [heroId]="1"> <hero-contact></hero-contact> </hero-
bio>
5. <hero-bio [heroId]="2"> <hero-contact></hero-contact> </hero-
bio>
6. <hero-bio [heroId]="3"> <hero-contact></hero-contact> </hero-
bio>`,
7. providers: [HeroService]
8. })
9. export class HeroBiosAndContactsComponent {

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 13/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

10. constructor(logger: LoggerService) {


11. logger.logInfo('Creating HeroBiosAndContactsComponent');
12. }
13. }

Focus on the template:

template: `
<hero-bio [heroId]="1"> <hero-contact></hero-contact> </hero-
bio>
<hero-bio [heroId]="2"> <hero-contact></hero-contact> </hero-
bio>
<hero-bio [heroId]="3"> <hero-contact></hero-contact> </hero-
bio>`,

We've inserted a <hero-contact> element between the <hero-bio> tags.


Angular projects (transcludes) the corresponding HeroContactComponent into the
HeroBioComponent view, placing it in the <ng-content> slot of the

HeroBioComponent template:

app/hero-bio.component.ts (template)

template: `
<h4>{{hero.name}}</h4>
<ng-content></ng-content>
<textarea cols="25" [(ngModel)]="hero.description"></textarea>`,

It looks like this, with the hero's telephone number from HeroContactComponent
projected above the hero description:

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 14/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

Here's the HeroContactComponent which demonstrates the qualifying decorators


that we're talking about in this section:

app/hero-contact.component.ts

1. @Component({
2. selector: 'hero-contact',
3. template: `
4. <div>Phone #: {{phoneNumber}}
5. <span *ngIf="hasLogger">!!!</span></div>`
6. })
7. export class HeroContactComponent {
8.

9. hasLogger = false;
10.

11. constructor(
12. @Host() // limit to the host component's instance of the
HeroCacheService
13. private heroCache: HeroCacheService,
14.

15. @Host() // limit search for logger; hides the


application-wide logger
16. @Optional() // ok if the logger doesn't exist
17. private loggerService: LoggerService
18. ) {
19. if (loggerService) {
20. this.hasLogger = true;
21. loggerService.logInfo('HeroContactComponent can log!');
22. }
23. }
24.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 15/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

25. get phoneNumber() { return this.heroCache.hero.phone; }


26.

27. }

Focus on the constructor parameters

app/hero-contact.component.ts

@Host() // limit to the host component's instance of the


HeroCacheService
private heroCache: HeroCacheService,

@Host() // limit search for logger; hides the application-wide


logger
@Optional() // ok if the logger doesn't exist
private loggerService: LoggerService

The @Host() function decorating the heroCache property ensures that we get a
reference to the cache service from the parent HeroBioComponent . Angular
throws if the parent lacks that service, even if a component higher in the component
tree happens to have that service.

A second @Host() function decorates the loggerService property. We know the


only LoggerService instance in the app is provided at the AppComponent level.
The host HeroBioComponent doesn't have its own LoggerService provider.

Angular would throw an error if we hadn't also decorated the property with the
@Optional() function. Thanks to @Optional() , Angular sets the

loggerService to null and the rest of the component adapts.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 16/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

We'll come back to the elementRef property shortly.

Here's the HeroBiosAndContactsComponent in action.

If we comment out the @Host() decorator, Angular now walks up the injector
ancestor tree until it 嘀nds the logger at the AppComponent level. The logger logic
kicks in and the hero display updates with the gratuitous "!!!", indicating that the
logger was found.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 17/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

On the other hand, if we restore the @Host() decorator and comment out
@Optional , the application fails for lack of the required logger at the host

component level.
EXCEPTION: No provider for LoggerService! (HeroContactComponent -

> LoggerService)

Inject the component's element


On occasion we might need to access a component's corresponding DOM element.
Although we strive to avoid it, many visual effects and 3rd party tools (such as
jQuery) require DOM access.

To illustrate, we've written a simpli嘀ed version of the HighlightDirective from


the Attribute Directives chapter.

app/highlight.directive.ts

1. import { Directive, ElementRef, HostListener, Input } from


'@angular/core';
2.

3. @Directive({
4. selector: '[myHighlight]'
5. })
6. export class HighlightDirective {
7.

8. @Input('myHighlight') highlightColor: string;


9.

10. private el: HTMLElement;


11.

12. constructor(el: ElementRef) {


13. this.el = el.nativeElement;
14. }
15.

16. @HostListener('mouseenter') onMouseEnter() {

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 18/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

17. this.highlight(this.highlightColor || 'cyan');


18. }
19.

20. @HostListener('mouseleave') onMouseLeave() {


21. this.highlight(null);
22. }
23.

24. private highlight(color: string) {


25. this.el.style.backgroundColor = color;
26. }
27. }

The directive sets the background to a highlight color when the user mouses over
the DOM element to which it is applied.

Angular set the constructor's el parameter to the injected ElementRef which is a


wrapper around that DOM element. Its nativeElement property exposes the DOM
element for the directive to manipulate.

The sample code applies the directive's myHighlight attribute to two <div> tags,
嘀rst without a value (yielding the default color) and then with an assigned color
value.

app/app.component.html (highlight)

<div id="highlight" class="di-component" myHighlight>


<h3>Hero Bios and Contacts</h3>
<div myHighlight="yellow">
<hero-bios-and-contacts></hero-bios-and-contacts>
</div>
</div>

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 19/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

The following image shows the effect of mousing over the <hero-bios-and-
contacts> tag.

De嘀ne dependencies with providers


In this section we learn to write providers that deliver dependent services.

Background
We get a service from a dependency injector by giving it a token.

We usually let Angular handle this transaction for us by specifying a constructor


parameter and its type. The parameter type serves as the injector lookup token.
Angular passes this token to the injector and assigns the result to the parameter.
Here's a typical example:

app/hero-bios.component.ts (component constructor injection)

constructor(logger: LoggerService) {
logger.logInfo('Creating HeroBiosComponent');
}

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 20/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

Angular asks the injector for the service associated with the LoggerService and
and assigns the returned value to the logger parameter.

Where did the injector get that value? It may already have that value in its internal
container. If it doesn't, it may be able to make one with the help of a provider. A
provider is a recipe for delivering a service associated with a token.

If the injector doesn't have a provider for the requested token, it delegates
the request to its parent injector, where the process repeats until there are
no more injectors. If the search is futile, the injector throws an error ...
unless the request was optional.

Let's return our attention to providers themselves.

A new injector has no providers. Angular initializes the injectors it creates with some
providers it cares about. We have to register our own application providers manually,
usually in the providers array of the Component or Directive metadata:

app/app.component.ts (providers)

providers: [ LoggerService, UserContextService, UserService ]

De嘀ning providers
The simple class provider is the most typical by far. We mention the class in the
providers array and we're done.

app/hero-bios.component.ts (class provider)

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 21/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

providers: [HeroService]

It's that simple because the most common injected service is an instance of a class.
But not every dependency can be satis嘀ed by creating a new instance of a class. We
need other ways to deliver dependency values and that means we need other ways
to specify a provider.

The HeroOfTheMonthComponent example demonstrates many of the alternatives


and why we need them.

It's visually simple: a few properties and the output of a logger. The code behind it
gives us plenty to talk about.

hero-of-the-month.component.ts

1. import { Component, Inject } from '@angular/core';


2.

3. import { DateLoggerService,
4. MinimalLogger } from './date-logger.service';
5. import { Hero } from './hero';
6. import { HeroService } from './hero.service';
7. import { LoggerService } from './logger.service';
8. import { RUNNERS_UP,
9. runnersUpFactory } from './runners-up';

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 22/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

10.

11. @Component({
12. selector: 'hero-of-the-month',
13. template: template,
14. providers: [
15. { provide: Hero, useValue: someHero },
16. { provide: TITLE, useValue: 'Hero of the Month' },
17. { provide: HeroService, useClass: HeroService },
18. { provide: LoggerService, useClass: DateLoggerService },
19. { provide: MinimalLogger, useExisting: LoggerService },
20. { provide: RUNNERS_UP, useFactory: runnersUpFactory(2),
deps: [Hero, HeroService] }
21. ]
22. })
23. export class HeroOfTheMonthComponent {
24. logs: string[] = [];
25.

26. constructor(
27. logger: MinimalLogger,
28. public heroOfTheMonth: Hero,
29. @Inject(RUNNERS_UP) public runnersUp: string,
30. @Inject(TITLE) public title: string)
31. {
32. this.logs = logger.logs;
33. logger.logInfo('starting up');
34. }
35. }

THE PROVIDE OBJECT LITERAL

The provide object literal takes a token and a de嘀nition object. The token is
usually a class but it doesn't have to be.

The de嘀nition object has one main property, (e.g. useValue ) that indicates how the

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 23/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

provider should create or return the provided value.

USEVALUE - THE VALUE PROVIDER

Set the useValue property to a 嘀xed value that the provider can return as the
dependency object.

Use this technique to provide runtime con嘀guration constants such as web-site base
addresses and feature 쀀ags. We often use a value provider in a unit test to replace a
production service with a fake or mock.

The HeroOfTheMonthComponent example has two value providers. The 嘀rst


provides an instance of the Hero class; the second speci嘀es a literal string
resource:

{ provide: Hero, useValue: someHero },


{ provide: TITLE, useValue: 'Hero of the Month' },

The Hero provider token is a class which makes sense because the value is a
Hero and the consumer of the injected hero would want the type information.

The TITLE provider token is not a class. It's a special kind of provider lookup key
called an OpaqueToken. We often use an OpaqueToken when the dependency is a
simple value like a string, a number, or a function.

The value of a value provider must be de嘀ned now. We can't create the value later.
Obviously the title string literal is immediately available. The someHero variable in
this example was set earlier in the 嘀le:

const someHero = new Hero(42, 'Magma', 'Had a great month!', '555-

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 24/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

555-5555');

The other providers create their values lazily when they're needed for injection.

USECLASS - THE CLASS PROVIDER

The useClass provider creates and returns new instance of the speci嘀ed class.

Use this technique to substitute an alternative implementation for a common or


default class. The alternative could implement a different strategy, extend the
default class, or fake the behavior of the real class in a test case.

We see two examples in the HeroOfTheMonthComponent :

{ provide: HeroService, useClass: HeroService },


{ provide: LoggerService, useClass: DateLoggerService },

The 嘀rst provider is the de-sugared, expanded form of the most typical case in which
the class to be created ( HeroService ) is also the provider's injection token. We
wrote it in this long form to de-mystify the preferred short form.

The second provider substitutes the DateLoggerService for the


LoggerService . The LoggerService is already registered at the

AppComponent level. When this component requests the LoggerService , it

receives the DateLoggerService instead.

This component and its tree of child components receive the


DateLoggerService instance. Components outside the tree continue to

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 25/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

receive the original LoggerService instance.

The DateLoggerService inherits from LoggerService ; it appends the current


date/time to each message:

app/date-logger.service.ts

@Injectable()
export class DateLoggerService extends LoggerService implements
MinimalLogger
{
logInfo(msg: any) { super.logInfo(stamp(msg)); }
logDebug(msg: any) { super.logInfo(stamp(msg)); }
logError(msg: any) { super.logError(stamp(msg)); }
}

function stamp(msg: any) { return msg + ' at ' + new Date(); }

USEEXISTING - THE ALIAS PROVIDER

The useExisting provider maps one token to another. In effect, the 嘀rst token is
an alias for the service associated with second token, creating two ways to access
the same service object.

{ provide: MinimalLogger, useExisting: LoggerService },

Narrowing an API through an aliasing interface is one important use case for this
technique. We're aliasing for that very purpose here. Imagine that the
LoggerService had a large API (it's actually only three methods and a property).
https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 26/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

We want to shrink that API surface to just the two members exposed by the
MinimalLogger class-interface:

app/date-logger.service.ts (MinimalLogger)

// class used as a restricting interface (hides other public


members)
export abstract class MinimalLogger {
logInfo: (msg: string) => void;
logs: string[];
}

The constructor's logger parameter is typed as MinimalLogger so only its two


members are visible in TypeScript:

Angular actually sets the logger parameter to the injector's full version of the
LoggerService which happens to be the DateLoggerService thanks to the

override provider registered previously via useClass . The following image, which
displays the logging date, con嘀rms the point:

USEFACTORY - THE FACTORY PROVIDER

The useFactory provider creates a dependency object by calling a factory

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 27/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

function as seen in this example.

{ provide: RUNNERS_UP, useFactory: runnersUpFactory(2), deps:


[Hero, HeroService] }

Use this technique to create a dependency object with a factory function whose
inputs are some combination of injected services and local state.

The dependency object doesn't have to be a class instance. It could be anything. In


this example, the dependency object is a string of the names of the runners-up to the
"Hero of the Month" contest.

The local state is the number 2 , the number of runners-up this component should
show. We execute runnersUpFactory immediately with 2 .

The runnersUpFactory itself isn't the provider factory function. The true provider
factory function is the function that runnersUpFactory returns.

runners-up.ts (excerpt)

export function runnersUpFactory(take: number) {


return (winner: Hero, heroService: HeroService): string => {
/* ... */
};
};

That returned function takes a winning Hero and a HeroService as arguments.

Angular supplies these arguments from injected values identi嘀ed by the two tokens
in the deps array. The two deps values are tokens that the injector uses to provide

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 28/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

these factory function dependencies.

After some undisclosed work, the function returns the string of names and Angular
injects it into the runnersUp parameter of the HeroOfTheMonthComponent .

The function retrieves candidate heroes from the HeroService , takes 2


of them to be the runners-up, and returns their concatenated names. Look
at the live example for the full source code.

Provider token alternatives: the class-interface and


OpaqueToken
Angular dependency injection is easiest when the provider token is a class that is
also the type of the returned dependency object (what we usually call the service).

But the token doesn't have to be a class and even when it is a class, it doesn't have
to be the same type as the returned object. That's the subject of our next section.

class-interface
In the previous Hero of the Month example, we used the MinimalLogger class as
the token for a provider of a LoggerService .

{ provide: MinimalLogger, useExisting: LoggerService },

The MinimalLogger is an abstract class.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 29/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

// class used as a restricting interface (hides other public


members)
export abstract class MinimalLogger {
logInfo: (msg: string) => void;
logs: string[];
}

We usually inherit from an abstract class. But LoggerService doesn't inherit from
MinimalLogger . No class inherits from it. Instead, we use it like an interface.

Look again at the declaration for DateLoggerService

export class DateLoggerService extends LoggerService implements


MinimalLogger

DateLoggerService inherits (extends) from LoggerService , not

MinimalLogger . The DateLoggerService implements MinimalLogger as if

MinimalLogger were an interface.

We call a class used in this way a class-interface. The key bene嘀t of a class-interface
is that we can get the strong-typing of an interface and we can use it as a provider
token in the same manner as a normal class.

A class-interface should de嘀ne only the members that its consumers are allowed to
call. Such a narrowing interface helps decouple the concrete class from its
consumers. The MinimalLogger de嘀nes just two of the LoggerClass members.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 30/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

WHY MINIMALLOGGER IS A CLASS AND NOT AN INTERFACE

We can't use an interface as a provider token because interfaces are not


JavaScript objects. They exist only in the TypeScript design space. They
disappear after the code is transpiled to JavaScript.

A provider token must be a real JavaScript object of some kind: a function,


an object, a string ... a class.

Using a class as an interface gives us the characteristics of an interface in


a JavaScript object.

The minimize memory cost, the class should have no implementation. The
MinimalLogger transpiles to this unoptimized, pre-mini嘀ed JavaScript:

var MinimalLogger = (function () {


function MinimalLogger() {}
return MinimalLogger;
}());
exports("MinimalLogger", MinimalLogger);

It never grows larger no matter how many members we add as long as


they are typed but not implemented.

OpaqueToken
Dependency objects can be simple values like dates, numbers and strings or
shapeless objects like arrays and functions.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 31/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

Such objects don't have application interfaces and therefore aren't well represented
by a class. They're better represented by a token that is both unique and symbolic, a
JavaScript object that has a friendly name but won't con쀀ict with another token that
happens to have the same name.

The OpaqueToken has these characteristics. We encountered them twice in the


Hero of the Month example, in the title value provider and in the runnersUp factory
provider.

{ provide: TITLE, useValue: 'Hero of the Month' },


{ provide: RUNNERS_UP, useFactory: runnersUpFactory(2), deps:
[Hero, HeroService] }

We created the TITLE token like this:

import { OpaqueToken } from '@angular/core';

export const TITLE = new OpaqueToken('title');

Inject into a derived class


We must take care when writing a component that inherits from another
component. If the base component has injected dependencies, we must re-provide
and re-inject them in the derived class and then pass them down to the base class
through the constructor.

In this contrived example, SortedHeroesComponent inherits from


HeroesBaseComponent to display a sorted list of heroes.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 32/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

The HeroesBaseComponent could stand on its own. It demands its own instance
of the HeroService to get heroes and displays them in the order they arrive from
the database.

app/sorted-heroes.component.ts (HeroesBaseComponent)

1. @Component({
2. selector: 'unsorted-heroes',
3. template: `<div *ngFor="let hero of heroes">{{hero.name}}</div>`,
4. providers: [HeroService]
5. })
6. export class HeroesBaseComponent implements OnInit {
7. constructor(private heroService: HeroService) { }
8.

9. heroes: Array<Hero>;
10.

11. ngOnInit() {
12. this.heroes = this.heroService.getAllHeroes();
13. this.afterGetHeroes();
14. }
15.

16. // Post-process heroes in derived class override.


17. protected afterGetHeroes() {}
18.

19. }

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 33/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

We strongly prefer simple constructors. They should do little more than


initialize variables. This rule makes the component safe to construct under
test without fear that it will do something dramatic like talk to the server.
That's why we call the HeroService from within the ngOnInit rather
than the constructor.

We explain the mysterious afterGetHeroes below.

Users want to see the heroes in alphabetical order. Rather than modify the original
component, we sub-class it and create a SortedHeroesComponent that sorts the
heroes before presenting them. The SortedHeroesComponent lets the base class
fetch the heroes. (we said it was contrived).

Unfortunately, Angular cannot inject the HeroService directly into the base class.
We must provide the HeroService again for this component, then pass it down to
the base class inside the constructor.

app/sorted-heroes.component.ts (SortedHeroesComponent)

1. @Component({
2. selector: 'sorted-heroes',
3. template: `<div *ngFor="let hero of heroes">{{hero.name}}</div>`,
4. providers: [HeroService]
5. })
6. export class SortedHeroesComponent extends HeroesBaseComponent {
7. constructor(heroService: HeroService) {
8. super(heroService);
9. }
10.

11. protected afterGetHeroes() {


12. this.heroes = this.heroes.sort((h1, h2) => {
13. return h1.name < h2.name ? -1 :
https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 34/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

14. (h1.name > h2.name ? 1 : 0);


15. });
16. }
17. }

Now take note of the afterGetHeroes method. Our 嘀rst instinct was to create an
ngOnInit method in SortedHeroesComponent and do the sorting there. But

Angular calls the derived class's ngOnInit before calling the base class's
ngOnInit so we'd be sorting the heroes array before they arrived. That produces a

nasty error.

Overriding the base class's afterGetHeroes method solves the problem

These complications argue for avoiding component inheritance.

Find a parent component by injection


Application components often need to share information. We prefer the more
loosely coupled techniques such as data binding and service sharing. But
sometimes it makes sense for one component to have a direct reference to another
component perhaps to access values or call methods on that component.

Obtaining a component reference is a bit tricky in Angular. Although an Angular


application is a tree of components, there is no public API for inspecting and
traversing that tree.

There is an API for acquiring a child reference (checkout Query , QueryList ,


ViewChildren , and ContentChildren ).

There is no public API for acquiring a parent reference. But because every
component instance is added to an injector's container, we can use Angular
https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 35/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

dependency injection to reach a parent component.

This section describes some techniques for doing that.

Find a parent component of known type


We use standard class injection to acquire a parent component whose type we
know.

In the following example, the parent AlexComponent has several children including
a CathyComponent :

parent-嘀nder.component.ts (AlexComponent v.1)

@Component({
selector: 'alex',
template: `
<div class="a">
<h3>{{name}}</h3>
<cathy></cathy>
<craig></craig>
<carol></carol>
</div>`,
})
export class AlexComponent extends Base
{
name= 'Alex';
}

Cathy reports whether or not she has access to Alex after injecting an
AlexComponent into her constructor:

parent-嘀nder.component.ts (CathyComponent)

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 36/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

@Component({
selector: 'cathy',
template: `
<div class="c">
<h3>Cathy</h3>
{{alex ? 'Found' : 'Did not find'}} Alex via the component
class.<br>
</div>`
})
export class CathyComponent {
constructor( @Optional() public alex: AlexComponent ) { }
}

We added the @Optional quali嘀er for safety but the live example con嘀rms that the
alex parameter is set.

Cannot 嘀nd a parent by its base class


What if we do not know the concrete parent component class?

A re-usable component might be a child of multiple components. Imagine a


component for rendering breaking news about a 嘀nancial instrument. For sound
(cough) business reasons, this news component makes frequent calls directly into
its parent instrument as changing market data stream by.

The app probably de嘀nes more than a dozen 嘀nancial instrument components. If
we're lucky, they all implement the same base class whose API our
NewsComponent understands.

Looking for components that implement an interface would be better.


That's not possible because TypeScript interfaces disappear from the

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 37/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

transpiled JavaScript which doesn't support interfaces. There's no artifact


we could look for.

We're not claiming this is good design. We are asking can a component inject its
parent via the parent's base class?

The sample's CraigComponent explores this question. Looking back we see that
the Alex component extends (inherits) from a class named Base .

parent-嘀nder.component.ts (Alex class signature)

export class AlexComponent extends Base

The CraigComponent tries to inject Base into its alex constructor parameter
and reports if it succeeded.

parent-嘀nder.component.ts (CraigComponent)

@Component({
selector: 'craig',
template: `
<div class="c">
<h3>Craig</h3>
{{alex ? 'Found' : 'Did not find'}} Alex via the base class.
</div>`
})
export class CraigComponent {
constructor( @Optional() public alex: Base ) { }
}

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 38/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

Unfortunately, this does not work. The live example con嘀rms that the alex
parameter is null. We cannot inject a parent by its base class.

Find a parent by its class-interface


We can 嘀nd a parent component with a class-interface.

The parent must cooperate by providing an alias to itself in the name of a class-
interface token.

Recall that Angular always adds a component instance to its own injector; that's why
we could inject Alex into Carol earlier.

We write an alias provider — a provide object literal with a useExisting


de嘀nition — that creates an alternative way to inject the same component instance
and add that provider to the providers array of the @Component metadata for
the AlexComponent :

parent-嘀nder.component.ts (AlexComponent providers)

providers: [{ provide: Parent, useExisting: forwardRef(() =>


AlexComponent) }],

Parent is the provider's class-interface token. The forwardRef breaks the circular
reference we just created by having the AlexComponent refer to itself.

Carol, the third of Alex's child components, injects the parent into its parent
parameter, the same way we've done it before:

parent-嘀nder.component.ts (CarolComponent class)

export class CarolComponent {

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 39/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

name= 'Carol';
constructor( @Optional() public parent: Parent ) { }
}

Here's Alex and family in action:

Find the parent in a tree of parents


Imagine one branch of a component hierarchy: Alice -> Barry -> Carol. Both Alice and
Barry implement the Parent class-interface.

Barry is the problem. He needs to reach his parent, Alice, and also be a parent to
Carol. That means he must both inject the Parent class-interface to get Alice and
provide a Parent to satisfy Carol.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 40/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

Here's Barry:

parent-嘀nder.component.ts (BarryComponent)

const templateB = `
<div class="b">
<div>
<h3>{{name}}</h3>
<p>My parent is {{parent?.name}}</p>
</div>
<carol></carol>
<chris></chris>
</div>`;

@Component({
selector: 'barry',
template: templateB,
providers: [{ provide: Parent, useExisting: forwardRef(() =>
BarryComponent) }]
})
export class BarryComponent implements Parent {
name = 'Barry';
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
}

Barry's providers array looks just like Alex's. If we're going to keep writing alias
providers like this we should create a helper function.

For now, focus on Barry's constructor:

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 41/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

It's identical to Carol's constructor except for the additional @SkipSelf decorator.

@SkipSelf is essential for two reasons:

1. It tell the injector to start its search for a Parent dependency in a component
above itself, which is what parent means.

2. Angular throws a cyclic dependency error if we omit the @SkipSelf decorator.

Cannot instantiate cyclic dependency! (BethComponent -> Parent

-> BethComponent)

Here's Alice, Barry and family in action:

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 42/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

The Parent class-interface


We learned earlier that a class-interface is an abstract class used as an interface
rather than as a base class.

Our example de嘀nes a Parent class-interface .

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 43/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

parent-嘀nder.component.ts (Parent class-interface)

export abstract class Parent { name: string; }

The Parent class-interface de嘀nes a name property with a type declaration but no
implementation., The name property is the only member of a parent component that
a child component can call. Such a narrowing interface helps decouple the child
component class from its parent components.

A component that could serve as a parent should implement the class-interface as


the AliceComponent does:

parent-嘀nder.component.ts (AliceComponent class signature)

export class AliceComponent implements Parent

Doing so adds clarity to the code. But it's not technically necessary. Although the
AlexComponent has a name property (as required by its Base class) its class

signature doesn't mention Parent :

parent-嘀nder.component.ts (AlexComponent class signature)

export class AlexComponent extends Base

The AlexComponent should implement Parent as a matter of proper


style. It doesn't in this example only to demonstrate that the code will
compile and run without the interface

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 44/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

A provideParent helper function


Writing variations of the same parent alias provider gets old quickly, especially this
awful mouthful with a forwardRef:

providers: [{ provide: Parent, useExisting: forwardRef(() =>


AlexComponent) }],

We can extract that logic into a helper function like this:

// Helper method to provide the current component instance in the


name of a `parentType`.
const provideParent =
(component: any) => {
return { provide: Parent, useExisting: forwardRef(() =>
component) };
};

Now we can add a simpler, more meaningful parent provider to our components:

providers: [ provideParent(AliceComponent) ]

We can do better. The current version of the helper function can only alias the
Parent class-interface. Our application might have a variety of parent types, each

with its own class-interface token.

Here's a revised version that defaults to parent but also accepts an optional

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 45/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

second parameter for a different parent class-interface.

// Helper method to provide the current component instance in the


name of a `parentType`.
// The `parentType` defaults to `Parent` when omitting the second
parameter.
const provideParent =
(component: any, parentType?: any) => {
return { provide: parentType || Parent, useExisting:
forwardRef(() => component) };
};

And here's how we could use it with a different parent type:

providers: [ provideParent(BethComponent, DifferentParent) ]

Break circularities with a forward class reference


(forwardRef)
The order of class declaration matters in TypeScript. We can't refer directly to a
class until it's been de嘀ned.

This isn't usually a problem, especially if we adhere to the recommended one class
per 嘀le rule. But sometimes circular references are unavoidable. We're in a bind
when class 'A refers to class 'B' and 'B' refers to 'A'. One of them has to be de嘀ned
嘀rst.

The Angular forwardRef function creates an indirect reference that Angular can
resolve later.

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 46/47
11/20/2016 Dependency Injection ­ ts ­ COOKBOOK

The Parent Finder sample is full of circular class references that are impossible to
break.

We face this dilemma when a class makes a reference to itself as does the
AlexComponent in its providers array. The providers array is a property of

the @Component decorator function which must appear above the class de嘀nition.

We break the circularity with forwardRef :

parent-嘀nder.component.ts (AlexComponent providers)

providers: [{ provide: Parent, useExisting: forwardRef(() =>


AlexComponent) }],

https://angular.io/docs/ts/latest/cookbook/dependency­injection.html 47/47

You might also like