Angular的`@ Host`装饰器没有达到顶峰? [英] Angular's `@Host` decorator not reaching the top?

查看:98
本文介绍了Angular的`@ Host`装饰器没有达到顶峰?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的主 app.ts 我已宣布全球提供商:

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

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



我还有一个组件:

  < my-app-component-3>来自3< / my-app-component-3>的Hello 

代码:

  @Component({
选择器:'my-app-component-3',
模板:`
< div> Component3:
< ng- content>< / ng-content>
:< span [innerHTML] =依赖??getName()>< / span>
< / div>


})
导出类Component3 {
构造函数(@Host()@ Option()公共依赖:依赖){}
}

结果是:


组件3:你好3:


但我希望结果是:



< blockquote>

组件3:3你好: AppModule提供商


因为基本上是应用结构是:

 < my-app> 
< my-app-component-3>
< / my-app-component-3>
< / my-app>

问题:

为什么不 @Host()匹配父提供商?



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



据我所知 - 注射器应该在此寻找依赖方式:





那么为什么不呢找到它?





用法



这decorator主要用于指令,以便从当前组件视图中的父注入器中解析提供程序。甚至单元测试也写完了仅测试指令。这是一个来自表单模块的实例,它是如何使用它的装饰器。



考虑<$ $的模板c $ c> A 组件:

 < form name =b> 
< input NgModel>
< / form>

NgModel 指令要解析提供的提供商通过表单指令。但是如果提供者不可用,则不需要超出当前组件 A



所以 NgModel 的定义如下:

  export class NgModel {
构造函数(@Optional()@ Host()parent:ControlContainer ...)

while form 指令定义如下:

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

此外,如果指令用 viewProviders定义,则指令可以注入由其托管组件定义的依赖项。例如,如果 MyApp 组件定义如下:

  @Component ({
选择器:'my-app',
viewProviders:[依赖关系],
模板:`< div provider-dir>< / div>`
})
导出类AppComponent {}

依赖将被解决。


In my main app.ts I've declared a global provider :

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

(Where createDependency is just a function that returns a class which has a getName() method.)

I also have a components :

    <my-app-component-3>Hello from 3</my-app-component-3>

Code :

@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) {}
}

The result is:

Component3: Hello from 3 :

But I expect the result to be :

Component3: Hello from 3 :AppModule provider

Because basically the app structure is :

<my-app>
  <my-app-component-3>
  </my-app-component-3>
</my-app> 

Question:
Why doesn't @Host() match the parent provider ?

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

To my knowledge - the injector should seek for a Dependency in this manner :

So why doesn't it find it ?

PLUNKER

Notice

I already know that if I remove @host - it does reach the top. My question is why adding @host - is not reaching the top - despite the fact thatmy-component3 is under my-app !!

解决方案

Check out A curios case of the @Host decorator and Element Injectors in Angular for in-depth explanation of how @Host decorator works and where Element Injectors come into this picture.

In order for it to work you should define dependencies in the in the parent component and using viewProviders:

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

Here is what the comments inside metadata.ts say:

Specifies that an injector should retrieve a dependency from any injector until reaching the host element of the current component.

So basically it says that a host element injector and all injectors above are not used when resolving a dependency. So if your MyApp component has the following template:

<my-app-component-3></my-app-component-3>

and the resulting components tree look like this:

<my-app>
    <my-app-component-3></my-app-component-3>
</my-app>

neither MyApp component's injector nor App module injectors are used to resolve dependency for the my-app-component-3.

However, there's the following interesting code in the ProviderElementContext._getDependency that performs one additional check:

// 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;
    }
}

which basically checks if the provider is defined in the viewProviders and resolves it if found. That's why viewProviders work.

So, here is the lookup tree:

Usage

This decorator is mostly used for directives to resolve providers from the parent injector within the current component view. Even the unit test is written only to test directives. Here is a real example from the forms module how it's decorator is used.

Consider this template for the A component:

<form name="b">
    <input NgModel>
</form>

NgModel directive wants to resolve a provider supplied by the form directive. But if the provider is not available, there's no need to go outside of a current component A.

So NgModel is defined like this:

export class NgModel {
    constructor(@Optional() @Host() parent: ControlContainer...)

While form directive is defined like this:

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

Also, a directive can inject dependencies defined by its hosting component if they are defined with viewProviders. For example, if MyApp component is defined like this:

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

the Dependency will be resolved.

这篇关于Angular的`@ Host`装饰器没有达到顶峰?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆