简体   繁体   English

如何使用@ViewChildren引用未知的子元素

[英]How to get reference to unknown children elements using @ViewChildren

I'm using a third party library that lets me create tabsets. 我正在使用第三方库来创建tabsets。 This is the code I'm using to create a simple tabset 这是我用来创建一个简单的tabset的代码

<clr-tabs           
   (clrTabsCurrentTabContentChanged)="onTabContentActivated($event)" >

  <clr-tab-link>Firewall</clr-tab-link>
  <clr-tab-link>DHCP</clr-tab-link>

  <clr-tab-content>
      <vcd-firewall-tab></vcd-firewall-tab>
  </clr-tab-content>

  <clr-tab-content>
     <vcd-dhcp-tab></vcd-dhcp-tab>
  </clr-tab-content>
</clr-tabs>

I have hooked up an event that will tell me when a tab was selected, and I would like to call the loadData() method on <vcd-firewall-tab> and <vcd-dhcp-tab> . 我已经连接了一个事件,告诉我何时选择了一个选项卡,我想在<vcd-firewall-tab><vcd-dhcp-tab>上调用loadData()方法。

The clrTabsCurrentTabContentChanged will give me a reference to the clr-tab-content that was selected but I would like to access its first child and call loadData() to implement lazy loading. clrTabsCurrentTabContentChanged将为我提供对所选clr-tab-content的引用,但我想访问它的第一个子loadData()并调用loadData()来实现延迟加载。

I think I can use @QueryChildren annotation, except that I have to specify the type of element to query for. 我想我可以使用@QueryChildren注释,除了我必须指定要查询的元素的类型。 The problem is that in this case, I don't know the type, it could be <vcd-firewall-tab> , <vcd-dhcp-tab> or the many other tabs that we have and I don't want to add custom code every time I add a new tab. 问题是,在这种情况下,我不知道类型,它可能是<vcd-firewall-tab><vcd-dhcp-tab>或我们拥有的许多其他选项卡,我不想添加每次添加新标签时自定义代码。

I was hoping to be able to do something like this from my event handler (but that doesn't exist 我希望能够从我的事件处理程序中做这样的事情(但这不存在

onTabContentActivated(tabContent: TabContent){
    (tabContent.query(':first-child') as CanLoadData).loadData();
}

I'm open to any suggestions, I thought maybe I could match the index of the tab to something like @QueryChildren('clr-tab-content > *') , assuming there's only one child under each tab. 我愿意接受任何建议,我想也许我可以将标签的索引与@QueryChildren('clr-tab-content > *')匹配,假设每个标签下只有一个孩子。

There are only two ways supported 只支持两种方式

  • passing a component or directive type 传递组件或指令类型
  • passing the name of a template variable 传递模板变量的名称

For other requirements you can inject ElementRef and access the DOM directly using ElementRef.nativeElement.... but this way you only get the element not components or directives. 对于其他需求,您可以使用ElementRef.nativeElement....注入ElementRef并直接访问DOM ElementRef.nativeElement....但这样您只能获得元素而不是组件或指令。

The real answer to the question, as mentioned by Günter , is that it can't be done. 正如Günter所提到的 ,这个问题的真正答案是它无法完成。 You cannot query unless you know the type ahead of time. 除非您提前知道类型,否则无法查询。

I hacked a solution to this particular problem minimizing the amount of code to add for each tab by doing two things 我破解了这个特定问题的解决方案,通过做两件事来最小化为每个选项卡添加的代码量

  • Creating a directive that can be added to the tabset, which will fire a DOM event on the child element of <clr-tab-contents> . 创建可以添加到tabset的指令,该指令将在<clr-tab-contents>的子元素上触发DOM事件。
  • Creating a function that does the hookup of the DOM event and calls the loadData() automatically when the tab is activated 创建一个执行DOM事件连接的函数,并在激活选项卡时自动调用loadData()

Here's a minimal example that implements that. 这是一个实现它的最小例子。 It's very crude, my real code takes care of other corner cases, but I didn't want to add noise to the solution. 它非常粗糙,我的真实代码处理其他极端情况,但我不想在解决方案中添加噪声。

<!-- One piece of glue per tabset -->
<clr-tabs vcd-lazy-tab-loader>
  <clr-tab-link>Firewall</clr-tab-link>
  <clr-tab-link>DHCP</clr-tab-link>

  <clr-tab-content>
      <vcd-firewall-tab></vcd-firewall-tab>
  </clr-tab-content>

  <clr-tab-content>
     <vcd-dhcp-tab></vcd-dhcp-tab>
  </clr-tab-content>
</clr-tabs>

// lazy-tab-loader.directive.ts
@Directive({
  selector: '[vcd-lazy-tab-loader]'
})

export class VcdLazyTabLoader {  
  constructor(@Inject(forwardRef(() => Tabs)) private tabSet: Tabs,
              private el: ElementRef) {
    tabSet.currentTabIndexChanged.subscribe((tabIndex) => {
      // Not very pretty, we'll find a nicer way later
      // It relies on the internal HTML structure of clr-tab-content
      const element = this.el.nativeElement.querySelectorAll(`clr-tab-content`)[tabIndex]                      
            .firstElementChild.firstElementChild;
      element.dispatchEvent(new CustomEvent("vcd-activated", {}));
    });
  }
}

export interface CanLoadData {
  loadData(): void;
}

export function setupLazyLoader(el: HTMLElement, dataLoader: CanLoadData) {
  el.addEventListener('vcd-activated', () => {
    dataLoader.loadData();
  });
}

// firewall-tab.component.ts (AND dhcp-tab.components.ts)
@Component(...)
// First piece of glue (implement CanLoadData) for a tab
class FirewallTab implements CanLoadData {

  constructor(private firewallService: FirewallService,
              private el: ElementRef) {
      // Second piece of glue, per tab
      // Constructor is not the best place, it's here just to avoid extra code
      setupLazyLoader(el.nativeElement, this);
  }
  loadData () {
    this.service.getRules().subscribe((data)=> this.rules = rules);
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM