[Angular] Control the dependency lookup with @Host, @SkipSelf and @Optional

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()@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.

@Host():

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(
    @Host()
    @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 @Host can use togerther with @Optional, so if the provider is not defined, then set it to null.

Lesson

猜你喜欢

转载自www.cnblogs.com/Answer1215/p/8856208.html
今日推荐