Difference between @Self and @Host Angular 2+ Depe

2019-03-19 19:56发布

Kindly explain the difference between @Self and @Host.

The angular API documentation gives some idea. But it's not clear to me. The example provided for Self uses ReflectiveInjector to exemplify usage.

However, one would rarely, if ever, use ReflectiveInjector in actual app code (probably more in testing).. Can you give an example of where you would use @Self instead of @Host outside of such test scenarios??

2条回答
对你真心纯属浪费
2楼-- · 2019-03-19 20:37

tl;dr

It looks like when @Self is used, Angular will only look for a value that is bound on the component injector for the element that this Directive/Component exists on.

It looks like when @Host is used, Angular will look for a value that is bound on either the component injector for the element that this Directive/Component exists on, or on the injector of the parent component. Angular calls this parent component the "host".

More explanation

Although the main descriptions aren't very helpful, it looks like the examples in the documentation for @Self and @Host do a decent job of clarifying how they are used and what the difference is (copied below).

When trying to understand this, it might help to remember that when Angular dependency injection tries to resolve a particular value for a constructor, it starts by looking in the injector for the current component, then it iterates upward through parent injectors. This is because Angular uses hierarchical injectors and allows for inheritance from ancestor injectors.

So when the @Host documentation says that it "specifies that an injector should retrieve a dependency from any injector until reaching the host element of the current component", that means that it stops this upward iteration early once it reaches the injector bound to the parent component.

@Self example (source)

class Dependency {}

@Injectable()
class NeedsDependency {
  constructor(@Self() public dependency: Dependency) {}
}

let inj = ReflectiveInjector.resolveAndCreate([Dependency, NeedsDependency]);
const nd = inj.get(NeedsDependency);

expect(nd.dependency instanceof Dependency).toBe(true);

inj = ReflectiveInjector.resolveAndCreate([Dependency]);
const child = inj.resolveAndCreateChild([NeedsDependency]);
expect(() => child.get(NeedsDependency)).toThrowError();

@Host example (source)

class OtherService {}
class HostService {}

@Directive({selector: 'child-directive'})
class ChildDirective {
  logs: string[] = [];

  constructor(@Optional() @Host() os: OtherService, @Optional() @Host() hs: HostService) {
    // os is null: true
    this.logs.push(`os is null: ${os === null}`);
    // hs is an instance of HostService: true
    this.logs.push(`hs is an instance of HostService: ${hs instanceof HostService}`);
  }
}

@Component({
  selector: 'parent-cmp',
  viewProviders: [HostService],
  template: '<child-directive></child-directive>',
})
class ParentCmp {
}

@Component({
  selector: 'app',
  viewProviders: [OtherService],
  template: '<parent-cmp></parent-cmp>',
})
class App {
}

Example of when using @Self is important

Let's say you have a directive that is used to modify the behavior of many types of components; maybe this directive provides some sort of configuration support.

This directive is bound to many components throughout your app and this directive binds some service in its providers list. Components that want to use this directive to dynamically configure themselves will inject the service it provides.

However, we want to make sure that a component only uses its own configuration, and doesn't accidentally inject the configuration service that was meant for some parent component. So we use the @Self decorator to tell Angular's dependency injection to only consider the configuration service provided on this component's element.

查看更多
冷血范
3楼-- · 2019-03-19 20:43

https://netbasal.com/exploring-the-various-decorators-in-angular-b208875b207c

Host:

@Host — The @Host decorator tells DI to look for a dependency in any injector until it reaches the host

Self:

@Self — The @Self decorator tells DI to look for a dependency only from itself, so it will not walk up the tree

here is an example:

https://plnkr.co/edit/UmpPTnzcRxgDc9Hn5I9G?p=preview

As you see MyDir directive use: @Self to access its own Car Its component' @Host Garage dependency @Optional @Host Sun dependency that is not defined on Host but defined on App. Since it is not defined on Host - it will be null

Output will be:

 parent component. 
  { "type": "child garage", 
    "car": { "model": "child car" }, 
    "sun": null 
  }

Here is components and providers:

  class Garage {
    car;
    type;
    sun;

    constructor(type) {
      this.type=type;
    }
    setCar(car) {
      this.car = car;
    }
    setSun(sun) {
      this.sun = sun;
    }
  }

  class Car {
    model;
    constructor(model) {
      this.model=model;
    }
  }

  class Sun { }

  @Directive({
    selector: '[myDir]',
    providers:[
      {provide: Car, useValue: new Car('child car')}
      {provide: Garage, useValue: new Garage('child garage')}
    ]
  })
  export class MyDir {
    constructor(@Self() private car: Car, @Host() private garage: Garage,
      @Optional() @Host() private sun: Sun) {
       this.garage.setCar(this.car);
       this.garage.setSun(this.sun);
    }
  }

  @Component({
    selector: 'parent',
    template: `
       parent component. {{garage|json}}
    `,
    providers:[
      {provide: Car, useValue: new Car('parent car')},
      {provide: Garage, useValue: new Garage('parent garage')}
    ]
  })
  export class Parent {
    childDep;
    constructor(private car: Car, private garage: Garage) {
    }
  }

  @Component({
    selector: 'my-app',
    template: `
  <parent myDir></parent>
    `,
    providers:[
      {provide: Car, useValue: new Car('app car')},
      {provide: Garage, useValue: new Garage('app garage')},
      {provide: Sun, useValue: 'sun'}
    ]
  })
  export class App {
  }
查看更多
登录 后发表回答