I first had a parent/child components that looked like this
Parent
<child-comp>
<div firstContent>This is my first content</div>
<div secondContent>This is my second content</div>
</child-comp>
Child
<div redColourDirective>
<div>This is the template with directive applied</div>
<ng-content select="[firstContent]"></ng-content>
<ng-content select="[secondContent]"></ng-content>
</div>
... then I wanted the directive, redColourDirective, to be conditional so the template became this (primarily because I can't have a conditional directive)
<ng-container *ngTemplateOutlet="withDirective() ? tmp1 : tmp2"> </ng-container>
<ng-template #tmp1>
<div redColourDirective>
<div>This is the template with directive applied</div>
<ng-content select="[firstContent]"></ng-content>
<ng-content select="[secondContent]"></ng-content>
</div>
</ng-template>
<ng-template #tmp2>
<div>
<div>This is the template WITHOUT directive applied</div>
<ng-content select="[firstContent]"></ng-content>
<ng-content select="[secondContent]"></ng-content>
</div>
</ng-template>
... where withDirective() is a signal as input. Now firstContent, secondContent project into the child component when withDirective is set to true from the parent but not when it is false. What am I doing wrong here?
//works, ng-content visible
<child-comp [withDirective]="true">
<div firstContent>This is my first content</div>
<div secondContent>This is my second content</div>
</child-comp>
//fails, no ng-content visible
<child-comp [withDirective]="false">
<div firstContent>This is my first content</div>
<div secondContent>This is my second content</div>
</child-comp>
What I've tried: Stackblitz demo
It is not recommended to use ng-content inside of conditionals or loops - Docs Reference
The fact that ng-content only grabs the two slots when it is dynamically rendered, I suppose, is the code's issue.
It won't render even after you try adding the directive to both conditionals, so the directive is not the root cause of the issue.
Using *ngTemplateOutlet
, which can render templates, in place of the ng-content
is a preferable approach.
<ng-container *ngTemplateOutlet="withDirective() ? tmp1 : tmp2"> </ng-container>
<ng-template #tmp1>
<div redColourDirective>
<div>This is the template with directive applied</div>
<ng-container *ngTemplateOutlet="firstContent()"></ng-container>
<ng-container *ngTemplateOutlet="secondContent()"></ng-container>
</div>
</ng-template>
<ng-template #tmp2>
<div>
<div>This is the template WITHOUT directive applied</div>
<ng-container *ngTemplateOutlet="firstContent()"></ng-container>
<ng-container *ngTemplateOutlet="secondContent()"></ng-container>
</div>
</ng-template>
Next, we pass in ng-template
s, which will include the content you wish to render dynamically, in place of divs.
<child-comp [withDirective]="false">
<ng-template #firstContent>This is my first content</ng-template>
<ng-template #secondContent>This is my second content</ng-template>
</child-comp>
<hr />
<child-comp [withDirective]="true">
<ng-template #firstContent>This is my first content</ng-template>
<ng-template #secondContent>This is my second content</ng-template>
</child-comp>
Lastly, we access this template and make it available to the child HTML by using the signal contentChild
.
import { Component, input, output, signal, contentChild } from '@angular/core';
import { Action } from './models';
import { NgTemplateOutlet } from '@angular/common';
import { RedColourDirective } from './colour-directive';
@Component({
selector: 'child-comp',
templateUrl: './child.component.html',
standalone: true,
imports: [NgTemplateOutlet, RedColourDirective],
})
export class ChildComponent {
firstContent: any = contentChild('firstContent');
secondContent: any = contentChild('firstContent');
withDirective = input<boolean>();
constructor() {}
}