angular: ContentChildren doesn't get children created with NgTemplateOutlet
Original title: ViewChildren doesn’t get children created with NgTemplateOutlet
Please see comments below…
I’m submitting a …
[x] bug report
Current behavior When you use ngTemplateOutlet to render a template passing by input property ViewChildren don’t get or don’t find in the redered elements.
Expected behavior I think ViewChildren should get the elements rendered by ngTemplateOutlet.
Minimal reproduction of the problem with instructions
In this plunkr, on app.ts file, you can see a Child
component and a Parent
component, Parent component has an input property called template1
, this accept a TemplateRef that is rendered using ngTemplateOutlet. Parent
component has a ViewChildren
collection to get the Child
components and shows the length of this collection in the end of template.
Well, using this component this way:
<ng-template #template1>
<child></child>
<child></child>
</ng-template>
<parent [template1]="template1"></parent>
The Parent
component render the two Child
components but the length of ViewChildren collection is 0. You can uncomment the line 16 of app.ts file to include a static <child/>
component in the Parent
template. Then the collection length is 1.
What is the motivation / use case for changing the behavior?
It’s would be very useful to pass template to components, rows of grids, lines of lists, …
In the other hand I have this plunkr in this case I am rendering Child
components using a ngFor
, ViewChildren works fine.
Since both, ngTemplateOutlet and ngForOf, using viewContainerRef.createEmbeddedView
to render the templates … why works with ngFor and not with ngTemplateOutlet?
I think that the only reason is that Child
components come from different component context, but I would like to know it.
Please tell us about your environment: Windows 10, VisualStudioCode, npm, lite-server
-
Angular version: 4.0.0-rc2
-
Browser: [all]
-
Language: [TypeScript 2.2.1]
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 119
- Comments: 31 (16 by maintainers)
@pkozlowski-opensource I understand
@ContentChildren
means in Angular. Is there any way to get the elements projected by a template?If I have this:
How to get the template content from
my-good-component
???In addition …
How is it possible that in version 7 of Angular it is still not possible to make this work? I can not believe it 😄
I have a list of issues that are more than two years open where this is requested or discussed …
@tolemac I believe that there is some confusion about what
@ContentChildren
means. In Angular content projection and queries it means “content as it was written in a template” and not “content as visible in the DOM” as you seem to expect.You should think about content nodes as additional input to the component (same as other
@Input
s) - a component can use its content to drive its template rendering. As such content is kind of contract between a component author and and a component user. A component author says “hey, if you use me with certain kind of nodes I can make use of them (project, query etc.)”. A component user (meaning: one using a component in a template) must understand the contract and provide (or not) nodes fulfilling a given contract. In other words the component usage place is where a component author and its user agree on the content nodes.The consequence of this mental model is that Angular only looks at the nodes that are children in a template (and not children in the rendered DOM). To calculate queries and content projection it is enough to inspect a template only without even inserting this content, ex.:
Your situation is similar, you just happen to have an additional template but the principles are the same:
the fact of inserting or not
#tplWithChild
doesn’t change anything - it is still outside of parent tags!Having said all this the good news is that I think that you can achieve what you want by moving template to be an actual child:
Here is a working stackblitz derived from your plunker: https://stackblitz.com/edit/angular-ebgnch?file=src%2Fapp%2Fapp.component.ts
I fully acknowledge that this can be confusing and I would love to make it easier on everyone. Let me know if the proposed solution works for your use-case and if the above explanation makes sense.
I will keep this issue open for a bit to see how we could better model / document this.
Perhaps it isn’t a bug, and it’s so by design.
I have tried to get children included by ngTemplateOutlet by two ways.
ViewChildren
andTemplateRef
Input
property rendering childs withngTemplateOutlet
(this issue).ngTemplateOutlet
to render to the content and usingContentChildren
:And I was searching about that and asking on stackoverflow (Q1 and Q2)
As you can see I’m thinking about this feature since 6 months ago. I think I haven’t written this issue prematurely.
The only way to get it, have been to use native elements.
If someone knows a better way to do it, I would be very grateful.
Thanks 😉
Any news on that? It could be very helpful to be able to query for stuff projected into components this way.
6 years and we dont have any way, how to query elements from dynamically created things…
I hope it will work AT LEAST with new @defer()…
Any news on this issue?
I have tried the workaround explained in the @edpeng comment but it does not work in my use case, maybe because of some implementation details of the PrimeNG Tree (I don’t know).
But I have found a very strange behaviour: at first when I check @ViewChildren in the @ngOnAfterViewInit it contains the children components (7), but after that the array becomes empty. I’m not sure, but it seems like the parent templates overwrite the child view.
(first variable is the @ViewChildren and second is the @ContentChildren)
EDIT: I have tried saving the initial @ViewChildren in a variable so I can manipulate it later, but it does not the trick because the change detection and template references get broken.
The only workaround I found is manipulating the children components with @ContentChildren { descendants: true } in the parent component, but it is not a clean solution considering the parent component should be able to set custom templates and these should be used in the child component.
Anyway it has a strange behaviour also, as the @ContentChildren is empty at first in the ngOnContentInit and then is filled with the projected templates. It does not make sense to me.
The good news is that at least it works and the user interface is in sync.
Definitely we would need an straightforward solution for this in Angular.
Encountered similar issue in Angular v6.0.0. Found this workaround based on subscribing to QueryList changes - similar to what @robertbrower-technologies suggested? I had to add an extra step to call ngDoCheck() because subscribe didn’t work when using *ngIf:
@feco93 For future reference he got the component reference by assigning it in the child’s constructor:
constructor(elem: ElementRef) { elem.nativeElement.component = this; }