Very differently to AngularJS (v1.x), Angular now has a hierarchical dependency injector. That allows to specify service definitions as well as the service lifetime at various levels in our application. Whenever a service is requested, Angular will walk up the component tree and search for a matching definition. While in most cases that's perfectly fine, often you may want to take control over the dependency lookup. In this lesson we will learn how, by applying"@Host, @Self()
, @SkipSelf()
and @Optional()
.
@Optional:
When using @Optional, it set logger to null if the LoggerService is not provided instead of error out.
export class PersonComponent implements OnInit { constructor( @Optional() public logger: LoggerService ) {} ngOnInit() {} doLog() { if (this.logger) { this.logger.log('Message from PersonComponent'); } else { console.log('sorry, no logger available'); } } }
@SkipSelf:
If child component and parent component both using the same provider and we want to skip using child component's provider instead using parent's provider.
// Child @Component({ selector: 'app-person', template: ` <div style="border:1px;"> <p *ngIf="logger === null">I don't have a logger</p> <button (click)="doLog()">write log</button> </div> ` providers: [ { provide: LoggerService, useFactory: loggerFactory('PersonComponent') } ] }) export class PersonComponent implements OnInit { constructor( @SkipSelf() @Optional() public logger: LoggerService ) {} ngOnInit() {} doLog() { if (this.logger) { this.logger.log('Message from PersonComponent'); } else { console.log('sorry, no logger available'); } } }
// parent @Component({ selector: 'app-root', template: ` <h1>Angular Services</h1> <app-person></app-person> `, providers: [ { provide: LoggerService, useFactory: loggerFactory('AppComponent') } ] }) export class AppComponent {}
SO in the end 'AppComponent ...' log message will appear in the console.
@Self():
Only using the provider for its own component.
@Component({ selector: 'app-person', template: ` <div style="border:1px;"> <p *ngIf="logger === null">I don't have a logger</p> <button (click)="doLog()">write log</button> </div> ` // providers: [ // { // provide: LoggerService, // useFactory: loggerFactory('PersonComponent') // } // ] }) export class PersonComponent implements OnInit { constructor( @Self() @Optional() public logger: LoggerService ) {} ngOnInit() {} doLog() { if (this.logger) { this.logger.log('Message from PersonComponent'); } else { console.log('sorry, no logger available'); } } }
So if PersonComponent has provider defined, it will use its own provider and will not continue searching parent component.
Often @Self can use togerther with @Optional, so if the provider is not defined, then set it to null.
@Host:
When we have directive, we might need to use @Host.
@Component({ selector: 'my-comp', ... providers: [ MyService // Must have, other directive cannot find it, throw error. ] })
<my-comp highlighted />
@Directive({ selector: 'highlighted' }) export class Hightlighted { // Use the provide inject directly into the host component constructor (@Host private myService: MyService) {} }
Because we cannot ensure that host element must have the Injection, if not, Angular will throw error, to prevent that, @Host normally work with @Optional together.
@Directive({ selector: 'highlighted' }) export class Hightlighted { // Use the provide inject directly into the host component constructor (@Optional @Host private myService: MyService) {} }