@Self和@Host Angular 2+依赖注入装饰器之间的区别

Som*_* S. 13 dependency-injection angular-decorator angular2-services angular

请解释之间的差异@Self@Host.

角度API文档提供了一些想法.但我不清楚.提供的示例Self使用ReflectiveInjector例举使用.

然而,人们很少,如果有的话,用ReflectiveInjector在实际的应用程序代码(可能更多的测试)..你能给的,你会用一个例子@Self 来代替@Host这样的测试场景之外?

Ric*_*nho 16

Angular 通过在元素注入器的层次结构中搜索它们来解决依赖关系,从当前元素的注入器开始,然后移动到父元素的注入器(如果在那里找不到),依此类推。如果仍未找到依赖项,它将移至模块注入器。如果在那里找不到它,则会抛出错误。https://angular.io/guide/hierarchical-dependency-injection#host

@Self 和 @Host 是修饰符,它们告诉 Angular 在哪些注入器上应该停止寻找依赖项。

@自己

@Self 告诉 Angular 它应该只在当前元素的注入器内查找。关于这一点需要注意的重要一点是,每个元素只有一个注入器,由附加到它的每个指令共享。因此,在此模板片段中:

<div dir-1 dir-2></div>
Run Code Online (Sandbox Code Playgroud)

假设dir-1对应于 Directive1,并dir-2对应于 Directive2,如果 Directive1 注册了一个提供者,那么 Directive2 将能够注入该服务,反之亦然。

如果依赖项具有 @Self 修饰符,则意味着 Angular 只会在当前元素的注入器中查找提供程序。除非@Optional修饰符也存在,否则如果找不到它就会抛出错误。

@Self 的用例是,如果您希望将服务注入到指令或组件中,前提是同一元素上的另一个指令提供了该服务。(该指令显然可以提供服务本身,但这似乎使 @Self 的使用有点多余)。

证明

https://stackblitz.com/edit/angular-di-test-4-6jxjas?file=src%2Fapp%2Fapp.component.html 考虑此模板app.component.html

<div my-directive-alpha>
    <h1 my-directive-beta my-directive-gamma>Lorem Ipsum..</h1>
</div>
Run Code Online (Sandbox Code Playgroud)

my-directive-alpha对应于MyDirectiveAlpha,my-directive-beta对应于MyDirectiveBeta,并my-directive-gamma对应于MyDirectiveGamma。

当 MyDirectiveGamma 尝试注入 MehProvider 时:

  constructor(@Self() meh: MehProvider) {
    console.log("gamma directive constructor:", meh.name);
  }
Run Code Online (Sandbox Code Playgroud)

MyDirectiveAlpha 和 MyDirectiveBeta 都在其提供程序数组中配置 MehProvider。如果从模板中删除 my-directive-beta,您将收到一条错误消息,指出 Angular 无法找到 MehProvider。如果您随后从 MyDirectiveGamma 中删除 @Self 装饰器,Angular 将从 MyDirectiveAlpha 中找到 MehProvider。因此,@Self 修饰符限制 Angular 只查看当前元素上的注入器。

@主持人

@Host 告诉 Angular 它应该停止寻找当前模板注入器之外的提供者。出于本文的目的,我将其称为模板注入器,但 Angular 的文档不使用该术语。该注入器包含来自组件的viewProviders数组的那些提供程序。组件还可能有一个提供者数组,它配置一个注入器,我将其称为组件注入器

所以对于这个组件:

<my-component></my-component>
Run Code Online (Sandbox Code Playgroud)

使用此模板:

<div>
  <h2>my component</h2>
  <div my-dir-1>
    <div my-dir-2>lorem ipsum...</div>
  </div>
</div>

Run Code Online (Sandbox Code Playgroud)

假设my-dir-1对应于 MyDirective1,并且my-dir-2对应于 MyDirective2,如果 MyDirective2 尝试注入使用 @Host 修饰符注释的依赖项:

constructor(@Host() foo: FooProvider) {
...
}
Run Code Online (Sandbox Code Playgroud)

然后 Angular 将搜索元素树中的所有元素注入器,但不会超出 MyComponent 的模板注入器。如果找不到提供者,再次假设 @Optional 修饰符不存在,则会抛出错误。

即使提供者存在于组件注入器中,仍然会抛出错误,因为 Angular 不会在那里搜索。因此我们可以得出结论,组件注入器比模板注入器高一个级别。

@Host 的用例是确保指令的包含组件可以控制特定服务的注入方式。

证明

https://stackblitz.com/edit/angular-di-host-modifier-proof?file=src%2Fapp%2Fmy-component%2Fmy-component.component.ts

考虑我的组件:

@Component({
  selector: "my-component",
  providers: [{provide: FooProvider, useValue: {name: 'FooProvider from providers'}}],
  viewProviders: [{provide: FooProvider, useValue: {name: 'FooProvider from view-providers'}}],
  template: `
    <div>
      <h2>This is my component</h2>
      <div>
        <h3 my-directive>Lorem Ipsum...</h3>
      </div>
    </div>
  `,

})
export class MyComponent {}
Run Code Online (Sandbox Code Playgroud)

my-directive对应于MyDirective。鉴于 MyDirective 尝试注入 FooProvider 并使用 @Host 修饰符:

  constructor(@Host() foo: FooProvider) {
    console.log("my directive:", foo.name);
  }

Run Code Online (Sandbox Code Playgroud)

注入的 FooProvider 的实际实例是来自 viewProviders 数组中的实例。如果我们注释掉这个数组,我们会得到一个错误,告诉我们 Angular 找不到提供者,即使它仍然存在于提供者数组中。因此,@Host 阻止 Angular 超越组件的模板注入器来寻找提供者。

  • 这里“@Self”的用例很棒——这对我来说是拼图中缺失的一块。这些装饰器似乎对指令比组件更有用 - 据我所知,这就是 Angular 在响应式表单实现中使用它们的方式 (2认同)

Lev*_*sey 15

TL;博士

看起来好像何时@Self使用,Angular将只查找该指令/组件所在元素的组件注入器上绑定的值.

看起来在@Host使用时,Angular将查找绑定在该指令/组件所在元素的组件注入器上或父组件的注入器上的值.Angular将此父组件称为"主机".

更多解释

虽然主要描述不是很有用,但看起来@Self@Host文档中的示例在澄清如何使用它们以及它们的区别(下面复制)方面做得不错.

当试图理解这一点时,可能有助于记住,当Angular依赖注入试图解析构造函数的特定值时,它首先查看注入器中的当前组件,然后通过父注入器向上迭代.这是因为Angular使用分层注入器并允许从祖先注入器继承.

因此,当@Host文档说它"指定注入器应该从任何注入器检索依赖关系直到到达当前组件的主机元素"时,这意味着它一旦到达绑定到父组件的注入器就会提前停止此向上迭代.

@Self例子(来源)

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();
Run Code Online (Sandbox Code Playgroud)

@Host示例(来源)

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 {
}
Run Code Online (Sandbox Code Playgroud)

使用@Self时的示例很重要

假设您有一个用于修改许多类型组件行为的指令; 也许这个指令提供了某种配置支持.

该指令绑定到整个应用程序中的许多组件,该指令将providers列表中的某些服务绑定.想要使用此指令动态配置自身的组件将注入它提供的服务.

但是,我们希望确保组件仅使用自己的配置,并且不会意外地注入针对某些父组件的配置服务.因此我们使用@Self装饰器告诉Angular的依赖注入只考虑该组件元素上提供的配置服务.

  • 我在[这个角度指南](https://angular.io/guide/dependency-injection-in-action#make-a-dependency-optional-and-limit-search-with-host)中找到了解释 _host component_ 非常好:`主机组件通常是请求依赖项的组件。但是,当该组件投影到父组件中时,该父组件将成为宿主。 (2认同)