如何使用@ViewChildren

时间:2016-10-07 14:25:40

标签: angular viewchild

我正在使用第三方库,可以让我创建标签集。这是我用来创建一个简单的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>

我已经联系了一个事件,告诉我何时选择了标签,并且我想在loadData()<vcd-firewall-tab>上调用<vcd-dhcp-tab>方法。

clrTabsCurrentTabContentChanged会给我一个对所选clr-tab-content的引用,但我想访问它的第一个孩子,并调用loadData()来实现延迟加载。

我想我可以使用@QueryChildren注释,除了我必须指定要查询的元素类型。问题是,在这种情况下,我不知道类型,它可能是<vcd-firewall-tab><vcd-dhcp-tab>或我们拥有的许多其他标签,我不想每次都添加自定义代码我添加了一个新标签。

我希望能够从我的事件处理程序中执行类似的操作(但这不存在)

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

我愿意接受任何建议,我想也许我可以将标签的索引与@QueryChildren('clr-tab-content > *')匹配,假设每个标签下只有一个孩子。

2 个答案:

答案 0 :(得分:1)

支持的方式只有两种

  • 传递组件或指令类型
  • 传递模板变量的名称

对于其他要求,您可以注入ElementRef并使用ElementRef.nativeElement....直接访问DOM,但这样您只能获得元素而不是组件或指令。

答案 1 :(得分:1)

问题as mentioned by Günter的真正答案是无法完成。除非您提前知道类型,否则无法查询。

我通过做两件事来破解这个特定问题的解决方案,最大限度地减少了为每个标签添加的代码量

  • 创建可添加到tabset的指令,该指令将在<clr-tab-contents>的子元素上触发DOM事件。
  • 创建一个执行DOM事件连接的函数,并在激活选项卡时自动调用loadData()

这是一个实现它的最小例子。它非常粗糙,我的真实代码处理其他极端情况,但我不想在解决方案中添加噪声。

<!-- 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);
  }
}