在组件树中获取对特定类型的所有指令的引用 [英] Obtaining references to all directives of a certain type up the component tree

查看:70
本文介绍了在组件树中获取对特定类型的所有指令的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个复杂的场景,需要帮助.

我有一个指令(称为TagDirective),该指令放置在我的应用程序的多个元素上.我还有另一个指令(QueryDirective),它需要引用其主机元素以及层次结构中位于其上方的所有元素上的所有TagDirective实例.

示例:

 <div appTag="a">
  <div appTag="b">
    <div appTag="c">
      <div appTag="d">
        <div appQuery>
          <!-- In here I want to have a reference to TagDirectives instances
              d,c,b,a -->
        </div>
      </div>
    </div>
    <div appTag="e">
      <div appTag="f">
        <div appTag="g">
          <div appTag="h">
            <div appQuery>
              <!-- In here I want to have a reference to TagDirectives instances
                  h,g,f,e,b,a -->
            </div>
          </div>
        </div>
        <div appQuery>
          <!-- In here I want to have a reference to TagDirectives instances
              f,e,b,a -->
        </div>
      </div>
    </div>
  </div>
</div>
 

我知道我可以通过使注入器在QueryDirective的构造函数中提供对单独在宿主元素上的TagDirective的引用,我也知道我可以通过注入ViewContainerRef获得下一个更高的实例,并且使用其parentInjector成员请求类型为TagDirective的实例.

但是,我还没有找到进一步深入树的方法,并一直收集所有实例直到根.

我将如何实现?谢谢!

解决方案

由于每个元素都有其自己的注入器,因此我们不能仅使用multi: true,只有在相同元素上提供相同标记的情况下,它才起作用./p>

可能的解决方法如下:

 export const TAG_DIRECTIVES_TOKEN = new InjectionToken('tags directives');

export function tagDirectiveFactory(dir: TagDirective, token: TagDirective[]) {
  return token ? [dir, ...token] : [dir];
}

@Directive({
  selector: '[appTag]',
  providers: [{
    provide: TAG_DIRECTIVES_TOKEN,
    useFactory: tagDirectiveFactory,
    deps: [TagDirective, [ new SkipSelf(), new Optional(), TAG_DIRECTIVES_TOKEN ]]
  }]
})
export class TagDirective  {}

@Directive({ 
  selector: '[appQuery]'
})
export class AppQueryDirective  {
  constructor(@Inject(TAG_DIRECTIVES_TOKEN) private directives: TagDirective[]){
      console.log(directives);
  }
}
 

Stackblitz示例

在上面的代码中,我在每个div[appTag]元素上提供TAG_DIRECTIVES_TOKEN.我使用带有以下依赖项的factory:

 deps: [TagDirective, [ new SkipSelf(), new Optional(), TAG_DIRECTIVES_TOKEN ]]
           ^^^^^                                            ^^^^^
current instance of TagDirective        parent optional TAG_DIRECTIVES_TOKEN  
 

其中:

  • SkipSelf告诉angular编译器跳过当前令牌并使用我们在父div[appTag]

  • 中提供的令牌 在这里使用
  • Optional是因为根div[appTag]元素无法识别父级TAG_DIRECTIVES_TOKEN,因此角度编译器不会引发错误:

没有提供InjectionToken标签指令的提供者!

I have a complex scenario which I need help with.

I have a directive (called TagDirective) that is placed on multiple elements all over my app. I have another directive (QueryDirective) which needs to reference all instances of TagDirective that exist on its host element, as well as on all the elements above it in the hierarchy.

Example:

<div appTag="a">
  <div appTag="b">
    <div appTag="c">
      <div appTag="d">
        <div appQuery>
          <!-- In here I want to have a reference to TagDirectives instances
              d,c,b,a -->
        </div>
      </div>
    </div>
    <div appTag="e">
      <div appTag="f">
        <div appTag="g">
          <div appTag="h">
            <div appQuery>
              <!-- In here I want to have a reference to TagDirectives instances
                  h,g,f,e,b,a -->
            </div>
          </div>
        </div>
        <div appQuery>
          <!-- In here I want to have a reference to TagDirectives instances
              f,e,b,a -->
        </div>
      </div>
    </div>
  </div>
</div>

I know I can obtain a reference to the TagDirective on the host element alone by having the injector provide it in the constructor of QueryDirective, I also know I can get the next higher instance by injecting ViewContainerRef and using its parentInjector member to request an instance of type TagDirective.

However, I haven't found a way to go further up the tree, and collect all the instances all the way up to the root.

How would I achieve that? Thanks!

解决方案

Since each of elements has its own injector we can't just use multi: true it will only work if we provide the same token on the same element.

Possible workaround could be as follows:

export const TAG_DIRECTIVES_TOKEN = new InjectionToken('tags directives');

export function tagDirectiveFactory(dir: TagDirective, token: TagDirective[]) {
  return token ? [dir, ...token] : [dir];
}

@Directive({
  selector: '[appTag]',
  providers: [{
    provide: TAG_DIRECTIVES_TOKEN,
    useFactory: tagDirectiveFactory,
    deps: [TagDirective, [ new SkipSelf(), new Optional(), TAG_DIRECTIVES_TOKEN ]]
  }]
})
export class TagDirective  {}

@Directive({ 
  selector: '[appQuery]'
})
export class AppQueryDirective  {
  constructor(@Inject(TAG_DIRECTIVES_TOKEN) private directives: TagDirective[]){
      console.log(directives);
  }
}

Stackblitz Example

In the code above i am providing TAG_DIRECTIVES_TOKEN on each of div[appTag] elements. I use factory with the following dependencies:

deps: [TagDirective, [ new SkipSelf(), new Optional(), TAG_DIRECTIVES_TOKEN ]]
           ^^^^^                                            ^^^^^
current instance of TagDirective        parent optional TAG_DIRECTIVES_TOKEN  

where:

  • SkipSelf tells angular compiler to skip current token and use token that we provided in the parent div[appTag]

  • Optional is used here because root div[appTag] element can't recognize parent TAG_DIRECTIVES_TOKEN so angular compiler won't raise the error:

No provider for InjectionToken tags directives!

这篇关于在组件树中获取对特定类型的所有指令的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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