记录一下学习使用 ChildContent 的试验代码,用的是 Angular 19。
AppComponent 是 parent component, SidebarComponent 是 child component,SidebarBlogCategoriesComponent 是 projected component 。
代码1:使用 ng-content 在 child component 中显示 projected component 的内容
// SidebarBlogCategoriesComponent
@Component({selector: 'cnb-sidebar-blog-categories',template: `<h1>Hello, World!</h1>`
})
export class SidebarBlogCategoriesComponent implements OnInit {constructor() { }ngOnInit() { }
}// SidebarComponent
@Component({selector: 'cnb-sidebar',template: `<p>SidebarComponent</p><ng-content></ng-content>`
})
export class SidebarComponent implements OnInit {@ContentChild(SidebarBlogCategoriesComponent) content?: SidebarBlogCategoriesComponent;constructor() { }ngOnInit() { }ngAfterContentInit(): void {console.log('content: ' + this.content);}
}// AppComponent
@Component({selector: 'app-root',imports: [SidebarComponent, SidebarBlogCategoriesComponent],template: `<p>AppComponent</p><cnb-sidebar><cnb-sidebar-blog-categories></cnb-sidebar-blog-categories></cnb-sidebar>`
})
export class AppComponent {
}
运行时页面显示结果:
AppComponent
SidebarComponent
Hello, World!
代码2:Directive + TemplateRef 无法显示 projected component 的内容
这是园子博客后台从 angular 15 升级到 angular 19 后遇到的问题,详见 https://q.cnblogs.com/q/151579
注:下面的代码中用 [ngTemplateOutlet]
取代了 <ng-content>
import { NgIf, NgTemplateOutlet } from '@angular/common';
import { Component, ContentChild, Directive, OnInit, TemplateRef } from '@angular/core';// SidebarContentDirective
@Directive({selector: '[cnbSidebarContent]',
})
export class SidebarContentDirective {constructor(public templateRef: TemplateRef<any>) { }
}// SidebarBlogCategoriesComponent
@Component({selector: 'cnb-sidebar-blog-categories',template: `<h1>Hello, World!</h1>`
})
export class SidebarBlogCategoriesComponent implements OnInit {constructor() { }ngOnInit() { }
}// SidebarComponent
@Component({selector: 'cnb-sidebar',imports: [NgIf, NgTemplateOutlet],template: `<p>SidebarComponent</p><div class="sidebar-content" *ngIf="content && content.templateRef"><ng-container [ngTemplateOutlet]="content.templateRef"></ng-container></div>`
})
export class SidebarComponent implements OnInit {@ContentChild(SidebarContentDirective) content?: SidebarContentDirective;constructor() { }ngOnInit() { }ngAfterContentInit(): void {console.log('content: ' + JSON.stringify(this.content));}
}@Component({selector: 'app-root',imports: [SidebarComponent, SidebarBlogCategoriesComponent],template: `<p>AppComponent</p><cnb-sidebar><cnb-sidebar-blog-categories *cnbSidebarContent></cnb-sidebar-blog-categories></cnb-sidebar>`
})
export class AppComponent {
}
页面输出:
AppComponent
SidebarComponent
console.log 的输出:
content: undefined
代码3:通过 ng-template 引用变量解决 Directive + TemplateRef 的问题
解决方法来自 stackoverflow 上 Angular content projection in standalone component 问题的回答:
We cannot use a directive on ng-template since it does not fire, ng-template is a virtual element and is not rendered in the DOM, so the better option, is to just create template reference variables like #cardHeader and #cardMainContent and access these through ContentChild and directly render them on the HTML.
@ContentChild('cardMainContent') cardMainContent!: TemplateRef<any>; @ContentChild('cardHeader') cardHeader!: TemplateRef<any>;
解决问题的示例代码:
import { NgIf, NgTemplateOutlet } from '@angular/common';
import { Component, ContentChild, Directive, OnInit, TemplateRef } from '@angular/core';// SidebarBlogCategoriesComponent
@Component({selector: 'cnb-sidebar-blog-categories',template: `<h1>Hello, World!</h1>`
})
export class SidebarBlogCategoriesComponent implements OnInit {constructor() { }ngOnInit() { }
}// SidebarComponent
@Component({selector: 'cnb-sidebar',imports: [NgIf, NgTemplateOutlet],template: `<p>SidebarComponent</p><div class="sidebar-content" *ngIf="content && content"><ng-container [ngTemplateOutlet]="content"></ng-container></div>`
})
export class SidebarComponent implements OnInit {@ContentChild('sidebarContent') content?: TemplateRef<any>;constructor() { }ngOnInit() { }ngAfterContentInit(): void {console.log('content: ' + JSON.stringify(this.content?.elementRef));}
}@Component({selector: 'app-root',imports: [SidebarComponent, SidebarBlogCategoriesComponent],template: `<p>AppComponent</p><cnb-sidebar><ng-template #sidebarContent><cnb-sidebar-blog-categories></cnb-sidebar-blog-categories></ng-template></cnb-sidebar>`
})
export class AppComponent {
}
页面显示结果:
AppComponent
SidebarComponent
Hello, World!
console.log 的输出:
content: {"nativeElement":{"__ngContext__":1}}