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.)