请解释@Self
和@Host
。
Angular API文档给出了一些想法。但这对我来说还不清楚。Self
提供的示例使用ReflectiveInjector
来体现用法。
但是,如果有的话,很少会在实际的应用代码中使用ReflectiveInjector
(可能在测试中更多(..您可以在此类 @Host
的 CC_7中提供一个示例测试方案??
tl; dr
看起来像是使用@Self
时,Angular只会寻找一个绑定在组件注射器上的值,用于该指令/组件存在的元素。
看起来像是使用@Host
时,Angular将寻找一个绑定在该指令/组件存在的元素的组件注射器上的值,或者在父级的注射器上存在的元素。Angular称此父组件为"主机"。
更多解释
尽管主要描述不是很有帮助,但看起来像@self和@host的文档中的示例做出了不错的工作,以澄清它们的使用方式以及有什么区别(以下复制(。
试图理解这一点时,可能会记住,当角依赖性注入试图解决构造函数的特定值时,它首先要在注射器中查找当前组件,然后通过父型注射器向上迭代。这是因为Angular使用层次注射器并允许祖先注射器继承。
因此,当@Host
文档说"指定喷油器应从任何注射器中检索依赖性直到到达当前组件的主机元素"时,这意味着它一旦到达注射器绑定到注射器,就可以尽早停止此迭代。父组件。
@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 {
}
何时使用@self很重要的示例
假设您有一个指令,该指令用于修改许多类型的组件的行为;也许该指令提供某种配置支持。
该指令绑定到整个应用程序中的许多组件,该指令在其providers
列表中绑定了某些服务。想要使用此指令动态配置自己的组件将注入其提供的服务。
但是,我们要确保组件仅使用其自己的配置,并且不会意外地注入适用于某些父组件的配置服务。因此,我们使用@Self
装饰器告诉Angular的依赖注入仅考虑该组件元素上提供的配置服务。
角度解决依赖性,通过在注射器上启动的元素注射器的层次结构搜索当前元素的元素,然后在父元素上移动到该元素,如果在那里找到的元素,等等,等等。如果仍未找到依赖关系,它将移至模块注射器上。如果在那里找不到,则会丢弃错误。https://angular.io/guide/hierarchical-dependenty-invoction#host
@self和@host是一个修饰符,可以告诉角度在哪个注射器上应该停止寻找依赖项。
@self
@self告诉Angular,它只能在当前元素上的注射器内看。要注意的一个重要点是,每个元素都有一个注射器,该喷油器由附加的每个指令共享。因此,在此模板段中:
<div dir-1 dir-2></div>
假设dir-1
对应于Directive1,而dir-2
对应于Directive2,如果Directive1注册提供商,则Directive2将能够注入该服务,反之亦然。
如果依赖关系具有@self修饰符,则意味着Angular仅在当前元素的注射器中查看提供商。除非也存在@Optional修饰符,否则如果找不到它,将丢弃错误。
@self的用例是,只有在同一元素上的另一个指令提供该服务时,只有将服务注入指令或组件。(该指令显然可以提供服务本身,但这似乎使@self的使用有些多余(。
证明
https://stackblitz.com/edit/angular-di-test-4-6jxjas?file=src/app/app/app.component.html在app.component.html
<div my-directive-alpha>
<h1 my-directive-beta my-directive-gamma>Lorem Ipsum..</h1>
</div>
让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);
}
myDirectiveAlpha和myDirectivebeta在其提供商数组中配置Mehprovider。如果您从模板中删除myDirective-beta,您会发现一个错误,说Angular找不到Mehprovider。如果您然后从mydirectivegamma中删除@self Decorator,Angular将从MyDirectiveAlpha中找到Mehprovider。因此,@self修饰符将角度限制为查看当前元素上的喷油器。
@host
@host告诉Angular,它应该停止寻找当前模板以外的注射器的提供商。出于本文的目的,我将其称为模板注入器,但是Angular的文档不使用此术语。该喷油器包含来自组件的 view -providers 数组的这些提供商。组件也可以具有提供程序数组,该数组配置了我称为组件注射器。
的喷油器。。因此,对于此组件:
<my-component></my-component>
使用此模板:
<div>
<h2>my component</h2>
<div my-dir-1>
<div my-dir-2>lorem ipsum...</div>
</div>
</div>
假设my-dir-1
对应于myDirective1,并且my-dir-2
对应于myDirective2,如果myDirective2尝试注入@Host修饰符注释的依赖关系:
constructor(@Host() foo: FooProvider) {
...
}
然后,Angular将通过元素树搜索所有元素注射器,但不会超越MyComponent的模板注射器。如果找不到提供商,则再次假设不存在@Optional修饰符,那么将丢弃错误。
即使提供商在组件注射器中存在,由于Angular不会在此处搜索,即使提供商存在。因此,我们可以得出结论,组件注射器是模板喷射器上方的水平。
@host的用例是确保指令的包含组件可以控制特定服务的注入。
证明
https://stackblitz.com/edit/angular-di-host-modifier-profier-profier?
考虑myComponent:
@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 {}
令my-directive
对应于myDirextive。鉴于MyDiractive尝试注入foooprovider并使用@host修饰符:
constructor(@Host() foo: FooProvider) {
console.log("my directive:", foo.name);
}
注入的Fooprovider的实际实例是从ViewProviders数组中。如果我们评论此数组,我们会收到一个错误,即即使在提供商数组中仍然存在Angular,也无法找到Angular。因此,@host防止Angular向提供商的组件的模板注射器外观。
https://netbasal.com/exploring-the-various-decorators-in-angular-b208875b207c
主机:
@host - @Host Decorator告诉DI在任何注射器到达主机
之前寻找依赖性
自我:
@self- @self Decorator告诉DI仅从本身寻找依赖性,因此不会走上树
这是一个示例:
https://plnkr.co/edit/umppptnzcrxgdc9hn5i9g?p=preview
如您所见,MyDir指令使用:@self访问自己的汽车其组件' @host车库依赖性@Optional @host Sun依赖性未在主机上定义,而是在应用程序上定义。由于它未在主机上定义 - 它将为null
输出将是:
parent component.
{ "type": "child garage",
"car": { "model": "child car" },
"sun": null
}
这是组件和提供商:
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 {
}
@self可以使用指令/组件自助提供者。但是主机不是
@Directive({
selector: '[myDir]',
providers:[
{provide: Car, useValue: new Car('child car')}
]
})
export class Parent {
constructor(@Self() private car: Car){}
}
Decorator @Host()
仅在上下文中使用的组件和指令。它做类似的工作,就像装饰器@Self()
为服务所做的。