So I have a setup where I have a layout component that uses multi-slot approach making certain sections of my page customizable:
import {
Component,
OnInit,
Input,
ContentChild,
AfterViewInit,
} from '@angular/core';
import { FooterComponent } from '../footer/footer.component';
import { HeaderComponent } from '../header/header.component';
@Component({
selector: 'app-layout',
template: `<div>
<div *ngIf="header" style="border-style: dotted;">
<h2>Header:{{ header.title }}</h2>
<ng-content select="app-header"></ng-content>
</div>
<div>
<h2>Content</h2>
<ng-content></ng-content>
</div>
<p>End of Content</p>
<div *ngIf="footer" style="border-style: dashed;">
<h2>Footer</h2>
<ng-content select="app-footer"></ng-content>
</div>
</div>`,
})
export class LayoutComponent implements AfterViewInit {
@ContentChild(HeaderComponent)
public header?: HeaderComponent;
@ContentChild(FooterComponent)
public footer?: FooterComponent;
public ngAfterViewInit(): void {
console.log('Injected', this.header, this.footer);
}
}
Notice that I use @ContentChild to inject and display specific elements on demand.
Now I want to create a more specialized component that pre-populates the footer:
import { Component } from '@angular/core';
@Component({
selector: 'app-nested-layout',
template: `<app-layout>
<ng-content select="app-header" ngProjectAs="app-header"></ng-content>
<ng-content></ng-content>
<app-footer>
<p>Predefined Footer</p>
</app-footer>
</app-layout>
`,
})
export class NestedLayoutComponent {}
Then I would use NestedLayoutComponent like this:
<app-nested-layout>
<app-header title="Header">
<p>Header stuff</p>
</app-header>
Regular content
</app-nested-layout>
I use ngProjectAs here so that app-layout actually recognizes the content. The problem is that even though the ngProjectAs correctly places the content into the right place in the DOM. the @ContentChild annotation for injecting the header component will not work in LayoutComponent and yields an undefined.
Is this maybe related to this issue?
I have also put together the whole example into Stackblitz
Possible Solution
You could pass the
headerComponentas an @Input from theNestedLayoutComponentto theLayoutComponentand there you check whether theheaderis defined, which is a direct child of theLayoutComponentor theparentHeaderis defined which is the direct child of theNestedLayoutComponent.Here is my stackblitz example
Your NestedLayoutComponent Template:
Your LayoutComponent Class:
Your LayoutComponent Template:
Feel free pass the
HeaderComponentfurther to theNestedLayoutComponentby your favorite way.Explained
The @ContentChild header is
undefinedin theLayoutComponent, because theHeaderComponentin this case is not a content child of theLayoutComponent, but a content child of theNestedLayoutComponent. Even though angular projects the content in a child component by selector, still when querying with @ContentChild the content child element must be a real child in the component template. In your example underAppComponenttheapp-headeris a child ofapp-nested-layoutandapp-layoutinapp-nested-layoutis does not have theapp-headeras a child content.