'Is it intended that, in Angular, projected content is evaluated even if it is not rendered to the DOM?
I've constructed a number of scenarios in Stackblitz that you can view here: https://stackblitz.com/edit/angular-zdyy8v
The issue we're contending with in particular is Scenario B, where we have content in a parent component projected into a child component:
<app-my-child-b [isShowing]="isShowingB">
<p>Showing {{ logRenderOf('B') }}</p>
</app-my-child-b>
public isShowingB = false;
public logRenderOf (str: string): string {
console.log(`${str} is rendering`);
return str;
}
The child component does not actually show this content, because isShowingB
is false
.
Here is what that child looks like:
<ng-container *ngIf="isShowing">
<ng-content></ng-content>
</ng-container>
@Input() isShowing = false;
Despite the fact that it does not show this content, it still renders that content, as can be seen in the console log:
B is rendering
This makes sense, because I understand it evaluates <app-my-child-b ...> ... </app-my-child-b>
as a comment node, and there is nothing within the parent saying that it shouldn't be rendered. It's not until it gets projected into the child that it finds an *ngIf
. The child doesn't even need to use ng-content
, that function will get called regardless of what the child it's being projected into is.
Sensical or not, it's definitely unexpected behavior, and I suspect in most cases undesirable. It also seems very inconsistent with what we see elsewhere, in templates and so on. For example, Scenario D here does not trigger the console log message:
<ng-template #myChildD>
<p>Showing {{ logRenderOf('D') }}</p>
</ng-template>
<app-my-child-d [isShowing]="isShowingD" [template]="myChildD"></app-my-child-d>
It feels like Scenario B is a bug, but is it?
(I'm not sure if I should report it, and StackOverflow has a larger community, so I thought I'd ask around here first.)
Solution 1:[1]
In my opinion it is an expected behaviour.
See, when we use this in html
<app-my-child-b [isShowing]="isShowingB">
<p>Showing {{ logRenderOf('B') }}</p> <-- literally html code, runs here and whatever happens will be prjected
</app-my-child-b>
it will evaluate what should be projected so will "render" it right here and just projects the result which ends up in running that function. but since result is in ngIf it will not be shown.
Solution 2:[2]
As explained in the official documentation you have to use a template and a directive to handle conditional content projection.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Eric Aska |
Solution 2 | TahitianGabriel |