Querying ContentChildren of Unknown Type
To reference DOM elements in the component template from the component code, ViewChild and ViewChildren decorators can be used:
import { Slides, Slide } from 'ionic-angular';
import { Component, QueryList, ViewChild, ViewChildren } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: 'my-component.html'
})
export class MyComponent {
@ViewChild(Slides) slidesComponent: Slides;
@ViewChildren(Slide) slideComponents: QueryList<Slide>
}
Similarly, ContentChild and ContentChildren decorators will provide access to DOM elements in the component content, i.e. those declared in the template that is using the component:
<my-component>
<div>First</div>
<div>Second</div>
<div>Third</div>
</my-component>
All four decorators mentioned above support two types of selection criteria for matching the DOM elements:
- by component type,
- by template variable name.
With only these two options available, I had a hard time coming up with a way to use ContentChildren
in a scenario where the consumer should be able to tag some of the DOM elements that need to be processed by the component, e.g. have an animation applied to them:
<my-component>
<div>Tagged</div>
<div>Not tagged</div>
<div>
<div>Not tagged</div>
<div>Tagged</div>
</div>
</my-component>
Of course, component type is not a suitable selection criterion in this case. For some reason, I was firmly convinced that template variable names had to be unique, which would make them unsuitable as well. Since I was running out of ideas, I decided to try it any way, although I couldn't find any reference that would confirm or deny my belief:
<my-component>
<div #process>Tagged</div>
<div>Not tagged</div>
<div>
<div>Not tagged</div>
<div #process>Tagged</div>
</div>
</my-component>
It worked exactly as I needed it to: Angular did not complain because of duplicate names and the QueryList
returned references to both tagged elements:
import {
Component,
QueryList,
ContentChildren,
ElementRef,
AfterContentInit
} from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: 'my-component.html'
})
export class MyComponent implements AfterContentInit {
@ContentChildren('process') elementsToProcess: QueryList<ElementRef>
ngAfterContentInit(): void {
this.elementsToProcess.forEach(element => {
// process the element
});
}
}
Lesson learned: when you're not sure whether something will work, it might be worth try it out anyway before you start looking for alternative solutions.