Angular的`@ Host`装饰器没有达到顶峰?

Roy*_*mir 4 javascript angular-decorator angular-components angular

在我的主要内容中,app.ts我宣布了一个全球供应商:

providers: [{provide: Dependency, useValue: createDependency('AppModule provider')}]
Run Code Online (Sandbox Code Playgroud)

(其中createDependency只是一个返回具有getName()方法的类的函数.)

我也有一个组件:

    <my-app-component-3>Hello from 3</my-app-component-3>
Run Code Online (Sandbox Code Playgroud)

代码:

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

结果是:

组件3:你好3:

但我希望结果如下:

Component3:来自3的Hello:AppModule提供程序

因为基本上app结构是:

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

问题:
为什么@Host() 与父提供商不匹配?

(这是:providers: [{provide: Dependency, useValue: createDependency('AppModule provider')}])

据我所知 - 喷射器应该Dependency以这种方式寻求:

在此输入图像描述

那为什么不找到呢?

PLUNKER

注意

我已经知道,如果我删除@host- 它确实达到了顶峰.我的问题是为什么添加@host - 没有达到顶峰 - 尽管事实my-component3是在my-app!!

Max*_*kyi 7

查看Angular中@Host装饰器和Element Injectors的古玩案例,深入解释@Host装饰器的工作原理以及Element Injectors在这张图片中的位置.

为了使它工作,您应该在父组件中定义依赖项并使用viewProviders:

@Component({
  selector: 'my-app',
  viewProviders: [{provide: Dependency, useValue: createDependency('AppModule provider')}],
    ...
export class MyApp {}
Run Code Online (Sandbox Code Playgroud)

以下是metadata.ts中的注释:

指定注入器应从任何注入器检索依赖项,直到到达当前组件的host元素.

所以基本上它表示在解析依赖关系时使用主机元素注入器和上面的所有注入器.因此,如果您的MyApp组件具有以下模板:

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

结果组件树看起来像这样:

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

无论MyApp组件的注射器,也不应用模块注射器用于解决的依赖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;
    }
}
Run Code Online (Sandbox Code Playgroud)

它基本上检查是否在中定义了提供者viewProviders,如果找到则解析它.这就是为什么viewProviders工作.

所以,这是查找树:

在此输入图像描述

用法

此装饰器主要用于指令以从当前组件视图中的父注入器解析提供程序.甚至单元测试也写入测试指令.这是forms模块如何使用它的装饰器的一个真实例子.

考虑该A组件的此模板:

<form name="b">
    <input NgModel>
</form>
Run Code Online (Sandbox Code Playgroud)

NgModel指令想要解析指令提供的提供form程序.但如果提供者不可用,则无需超出当前组件A.

所以NgModel定义如下:

export class NgModel {
    constructor(@Optional() @Host() parent: ControlContainer...)
Run Code Online (Sandbox Code Playgroud)

虽然form指令定义如下:

@Directive({
  selector: '[formGroup]',
  providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
  ...
})
export class NgForm
Run Code Online (Sandbox Code Playgroud)

此外,如果定义了指令,则指令可以注入由其宿主组件定义的依赖项viewProviders.例如,如果MyApp组件定义如下:

@Component({
    selector: 'my-app',
    viewProviders: [Dependency],
    template: `<div provider-dir></div>`
})
export class AppComponent {}
Run Code Online (Sandbox Code Playgroud)

Dependency将得到解决.