Reputation: 92304
I'm using a third party library that lets me create tabsets. This is the code I'm using to create a simple 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>
.
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.
I think I can use @QueryChildren
annotation, except that I have to specify the type of element to query for. 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.
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.
Upvotes: 1
Views: 1725
Reputation: 92304
The real answer to the question, as mentioned by Günter, is that it can't be done. 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
<clr-tab-contents>
.loadData()
automatically when the tab is activatedHere'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);
}
}
Upvotes: 1
Reputation: 657731
There are only two ways supported
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.
Upvotes: 2