Content Projection
(or Slot in some other technologies)
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-content />
</div>
`,
})
export class MyCard {}
@Component({
selector: 'app-root',
standalone: true,
template: `
<my-card>
Card content
</my-card>
`,
imports: [MyCard]
})
export class App {}
@Component({
selector: 'app-root',
standalone: true,
template: `
<my-card>
<my-content />
</my-card>
`,
imports: [MyCard, MyContent]
})
export class App {}
@Component({
selector: 'app-root',
standalone: true,
template: `
<my-card>
<my-content />
</my-card>
`,
imports: [MyCard, MyContent]
})
export class App {}
Multi slots
@Component({
selector: 'my-card-header',
standalone: true,
template: '<ng-content />'
})
export class MyCardHeader {}
@Component({
selector: 'my-card-footer',
standalone: true,
template: '<ng-content />'
})
export class MyCardFooter {}
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-content select="my-card-header" />
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {}
Multi slots
@Component({
selector: 'app-root',
standalone: true,
template: `
<my-card>
<my-card-header>Header</my-card-header>
<my-content />
<my-card-footer>Footer</my-card-footer>
</my-card>
`,
imports: [MyCard, MyCardHeader, MyCardFooter, MyContent]
})
export class App {}
Multi slots
@Component({
selector: 'app-root',
standalone: true,
template: `
<my-card>
<my-card-header>Header</my-card-header>
<my-content />
<my-card-footer>Footer</my-card-footer>
</my-card>
`,
imports: [MyCard, MyCardHeader, MyCardFooter, MyContent]
})
export class App {}
Multi slots
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-content select="my-card-header" />
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {}
@Component({
selector: 'app-root',
standalone: true,
template: `
<my-card>
<my-card-header>Header</my-card-header>
<my-content />
<my-card-footer>Footer</my-card-footer>
</my-card>
`,
imports: [MyCard, MyCardHeader, MyCardFooter, MyContent]
})
export class App {}
Multi slots
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-content select="my-card-header" />
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {}
@Component({
selector: 'app-root',
standalone: true,
template: `
<my-card>
<my-card-footer>Footer</my-card-footer>
<my-content />
<my-card-header>Header</my-card-header>
</my-card>
`,
imports: [MyCard, MyCardHeader, MyCardFooter, MyContent]
})
export class App {}
Multi slots
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-content select="my-card-header" />
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {}
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-content select="my-card-header" />
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {}
Content Projection
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-content select="my-card-header" />
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {
@ContentChild(MyCardHeader) cardHeader?: MyCardHeader;
}
Content Projection
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<!-- <ng-content select="my-card-header" /> -->
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {
@ContentChild(MyCardHeader) cardHeader?: MyCardHeader;
}
Content Projection
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<!-- <ng-content select="my-card-header" /> -->
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {
@ContentChild(MyCardHeader) cardHeader?: MyCardHeader;
}
Content Projection
<my-card>
<my-card-header>Header</my-card-header>
<my-content />
<my-card-footer>Footer</my-card-footer>
</my-card>
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<!-- <ng-content select="my-card-header" /> -->
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {
@ContentChild(MyCardHeader) cardHeader?: MyCardHeader;
}
Content Projection
<my-card>
<my-card-header>Header</my-card-header>
<my-content />
<my-card-footer>Footer</my-card-footer>
</my-card>
not projected
query works
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<!-- <ng-content select="my-card-header" /> -->
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {
@ContentChild(MyCardHeader) cardHeader?: MyCardHeader;
}
Content Projection
<my-card>
<my-card-header>
Header
</my-card-header>
<my-content />
<my-card-footer>
Footer
</my-card-footer>
</my-card>
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<!-- <ng-content select="my-card-header" /> -->
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {
@ContentChild(MyCardHeader) cardHeader?: MyCardHeader;
}
Content Projection
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<!-- <ng-content select="my-card-header" /> -->
<ng-content />
<ng-content select="my-card-footer" />
</div>
`,
})
export class MyCard {
@ContentChild(MyCardHeader) cardHeader?: MyCardHeader;
@ContentChildren(ElementRef) elementList!: QueryList<ElementRef<any>>;
}
Content Projection
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-content />
</div>
`,
})
export class MyCard {}
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-container *ngTemplateOutlet="template" />
</div>
`,
imports: [NgTemplateOutlet]
})
export class MyCard {
@ContentChild(TemplateRef, { static: true }) template!: TemplateRef<unknown>;
}
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-container *ngTemplateOutlet="template" />
</div>
`,
imports: [NgTemplateOutlet]
})
export class MyCard {
@ContentChild(TemplateRef, { static: true }) template!: TemplateRef<unknown>;
}
😰
@Component({
selector: 'app',
standalone: true,
template: `
<my-card>
<ng-template> My Content </ng-template>
</my-card>
`,
imports: [MyCard]
})
export class App {}
@Directive({ selector: 'ng-template[my-card-header]', standalone: true })
export class MyCardHeader {}
@Directive({ selector: 'ng-template[my-card-footer]', standalone: true })
export class MyCardFooter {}
@Directive({ selector: 'ng-template[my-card-content]', standalone: true })
export class MyCardContent {}
@Component({
selector: 'my-card',
standalone: true,
template: `
<div class="card">
<ng-container *ngTemplateOutlet="headerTemplate" />
<ng-container *ngTemplateOutlet="template" />
<ng-container *ngTemplateOutlet="footerTemplate" />
</div>
`,
imports: [NgTemplateOutlet]
})
export class MyCard {
@ContentChild(MyCardContent, { static: true, read: TemplateRef })
template!: TemplateRef<unknown>;
@ContentChild(MyCardHeader, { static: true, read: TemplateRef })
headerTemplate!: TemplateRef<unknown>;
@ContentChild(MyCardFooter, { static: true, read: TemplateRef })
footerTemplate!: TemplateRef<unknown>;
}
Multi Slots
@Component({
selector: 'app',
standalone: true,
template: `
<my-card>
<ng-template my-card-header>
My header
</ng-template>
<ng-template my-card-content>
<my-content />
</ng-template>
<ng-template my-card-footer>
My footer
</ng-template>
</my-card>
`,
imports: [MyCard, MyCardHeader, MyCardFooter, MyCardContent, MyContent]
})
export class App {}
Multi Slots
@Component({
selector: 'app',
standalone: true,
template: `
<my-card>
<ng-template my-card-header>
My header
</ng-template>
<my-content *my-card-content />
<ng-template my-card-footer>
My footer
</ng-template>
</my-card>
`,
imports: [MyCard, MyCardHeader, MyCardFooter, MyCardContent, MyContent]
})
export class App {}
Multi Slots
Dynamic Context
Dynamic Context
Default Content
Inherit Injector
Inherit Injector
https://www.youtube.com/watch?v=XE9dI8gSi9U
Conditional Content
Conditional Content
Conditional Content