I'll start with my component's usage on a hosting component:
<my-templated-component [content-template]="contentTemplate">
</my-templated-component>
<ng-template #contentTemplate>
<h3>Hello There</h3>
<div>Lorem ipsum..... </div>
<custom-component></custom-component>
</ng-template>
Now, my component's .ts:
class MyTemplatedComponent implements AfterContentInit {
@Input('content-template') contentTemplate: TemplateRef<any>;
@ContentChildren(CustomComponent) customComponentQueryList: QueryList<CustomComponent>;
...
ngAfterContentInit() {
// Length is 0 at this point. My component didn't find the component I want
console.log(`There are ${this.customComponentQueryList.length} items`);
this.customComponentQueryList.changes.subscribe((s) => {
/* This is not called. This is where I would have wanted to find
my CustomComponent to perform a task that requires its presence.
*/
});
}
}
And here is the markup for MyTemplatedComponent:
<ng-container *ngTemplateOutlet="contentTemplate">
</ng-container>
I understand that the QueryList<CustomComponent> will only be available at ngAfterContentInit(). Without subscribing to it, CustomComponentQueryList has zero items, so it hasn't found a CustomComponent from the @Inputted TemplateRef. But the changes of the QueryList<CustomComponent> isn't triggered, so I definitely can't find my component.
The strange thing is that I see everything from that @Inputted TemplateRef on the page, including the markup from CustomComponent. So, MyTemplatedComponent does detect my inputted template ref and displays it through *ngTemplateOutlet, but the QueryList can't find my CustomComponent?
Now, however, if I changed @Input() contentTemplate: TemplateRef<any> to
@ContentChild('contentTemplate') contentTemplate: TemplateRef<any>, and then put the template inside the tags of <my-templated-component> like this:
<my-templated-component>
<ng-template #contentTemplate>
<h3>Hello There</h3>
<div>Lorem ipsum..... </div>
<custom-component></custom-component>
</ng-template>
</my-templated-component>
The QueryList changes does get triggered, and from the subscription, I can find my CustomComponent.
Perhaps @ContentChild and @ContentChildren will only work if and only if I straight-up put the <ng-template> as an immediate element child of my component's markup/selector as I've done above.
But I won't always get to plop <my-templated-component> this way, and there will be times where the <ng-template #contentTemplate> might even come from outside the hosting component.
Everything is fine if all I want to do is to take in TemplateRefs and display them somewhere in my templated components, but I'll need to investigate the contents of the TemplateRefs I'll receive for various purposes.
If @ContentChildren requires that I put my <ng-template>s inside my component's tags, what would be the equivalent decorator (I don't think there is one, and it's definitely not @ViewChildren), if the TemplateRef was passed via @Input? Any effective workarounds?
Ultimately, from inside templated components, how do we find certain components inside TemplateRefs passed in as @Input if @ContentChildren QueryList<T> don't work?