在我的主要 app.ts
中,我声明了一个全局提供者:
providers: [{provide: Dependency, useValue: createDependency('AppModule provider')}]
(createDependency
只是一个返回具有 getName()
方法的类的函数。)
我还有一个组件:
<my-app-component-3>Hello from 3</my-app-component-3>
代码:
@Component({
selector: 'my-app-component-3',
template: `
<div>Component3:
<ng-content></ng-content>
: <span [innerHTML]="dependency?.getName()"></span>
</div>
`,
})
export class Component3 {
constructor(@Host() @Optional() public dependency: Dependency) {}
}
结果是:
Component3: Hello from 3 :
但我希望结果是:
Component3: Hello from 3 :AppModule provider
因为基本上应用程序结构是:
<my-app>
<my-app-component-3>
</my-app-component-3>
</my-app>
问题:
为什么 @Host()
不匹配父提供者?
(即:providers: [{provide: Dependency, useValue: createDependency('AppModule provider')}]
)
据我所知 - 注入(inject)器应该以这种方式寻找 Dependency
:
那么为什么它找不到呢?
注意事项
我已经知道,如果我删除 @host
- 它会到达顶部。我的问题是为什么添加 @host - 没有到达顶部 - 尽管 my-component3
在 my-app
下!!
最佳答案
查看 A curios case of the @Host decorator and Element Injectors in Angular深入解释@Host 装饰器的工作原理以及元素注入(inject)器在这幅图中的作用。
为了使其正常工作,您应该在父组件 中定义依赖项并使用viewProviders:
@Component({
selector: 'my-app',
viewProviders: [{provide: Dependency, useValue: createDependency('AppModule provider')}],
...
export class MyApp {}
下面是里面的评论metadata.ts说:
Specifies that an injector should retrieve a dependency from any injector until reaching the host element of the current component.
所以基本上它表示在解析依赖项时,宿主元素注入(inject)器和上面的所有注入(inject)器都不会使用。因此,如果您的 MyApp
组件具有以下模板:
<my-app-component-3></my-app-component-3>
生成的组件树如下所示:
<my-app>
<my-app-component-3></my-app-component-3>
</my-app>
MyApp
组件的注入(inject)器和 App 模块注入(inject)器都用于解决 my-app-component-3
的依赖关系。
但是,ProviderElementContext._getDependency 中有以下有趣的代码执行一项额外的检查:
// check @Host restriction
if (!result) {
if (!dep.isHost || this.viewContext.component.isHost ||
this.viewContext.component.type.reference === tokenReference(dep.token !) ||
// this line
this.viewContext.viewProviders.get(tokenReference(dep.token !)) != null) { <------
result = dep;
} else {
result = dep.isOptional ? result = {isValue: true, value: null} : null;
}
}
它基本上检查提供者是否在 viewProviders
中定义,如果找到则解析它。这就是 viewProviders
起作用的原因。
所以,这是查找树:
用法
这个装饰器主要用于在当前组件 View 中解析来自父注入(inject)器的提供者的指令。即使是 unit test is written仅用于测试指令。这是 forms
模块中如何使用装饰器的真实示例。
考虑 A
组件的这个模板:
<form name="b">
<input NgModel>
</form>
NgModel
指令想要解析由 form
指令提供的提供者。但是,如果提供程序不可用,则无需超出当前组件 A
。
所以 NgModel
是这样定义的:
export class NgModel {
constructor(@Optional() @Host() parent: ControlContainer...)
虽然 form
指令是这样定义的:
@Directive({
selector: '[formGroup]',
providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
...
})
export class NgForm
此外,指令可以注入(inject)由其托管组件定义的依赖项,如果它们是使用 viewProviders
定义的。例如,如果 MyApp
组件定义如下:
@Component({
selector: 'my-app',
viewProviders: [Dependency],
template: `<div provider-dir></div>`
})
export class AppComponent {}
依赖
将被解析。
关于javascript - Angular 的 `@Host` 装饰器没有到达顶部?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47602697/