这是园子博客后台升级到 Angular 19 并迁移到 Standalone Components 后遇到的问题
posts-sidebar.component.ts 组件中的 <cnb-sidebar-blog-categories>
对应的 component 代码没有执行
@Component({
selector: 'cnb-posts-sidebar',
template: `
<ng-template cnbLayoutSidebar>
<cnb-sidebar header="博客后台"
[items]="operations"></cnb-sidebar>
<cnb-sidebar header="分类">
<cnb-sidebar-blog-categories *cnbSidebarContent></cnb-sidebar-blog-categories>
</cnb-sidebar>
</ng-template>
`,
imports: [SidebarComponent, SidebarBlogCategoriesComponent, LayoutSidebarDirective]
})
就是这部分,
<cnb-sidebar-blog-categories *cnbSidebarContent></cnb-sidebar-blog-categories>
cnbSidebarContent
是一个 @Directive,对应的实现代码如下:
@Directive({
selector: '[cnbSidebarContent]',
})
export class SidebarContentDirective {
constructor(public templateRef: TemplateRef<any>) { }
}
如果去掉 *cnbSidebarContent
directive,<cnb-sidebar-blog-categories>
的组件代码就会执行
问题与 <cnb-sidebar header="分类">
对应的 SidebarComponent
有关
SidebarComponent.ts
@Component({
selector: 'cnb-sidebar',
templateUrl: './sidebar.component.html'
})
export class SidebarComponent implements OnInit, OnDestroy {
@Input() header = '';
@Input() items: ISidebarItem[] = [];
@ContentChild(SidebarContentDirective) content?: SidebarContentDirective;
}
注:cnbSidebarContent
起作用是因为上面的 @ContentChild(SidebarContentDirective)
部分的代码
sidebar.component.html
<div class="header">
<!-- ... -->
<div class="sidebar-content"
*ngIf="content && content.templateRef && (!items || items.length <= 0)">
<ng-container [ngTemplateOutlet]="content.templateRef"></ng-container>
</div>
</div>
出问题时,上面的 content 的值是 undefined
终于解决了!问题的原因是 Standalone Component 不支持在 ViewChild 中使用 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>;
具体解决方法如下:
将
<cnb-sidebar header="分类">
<cnb-sidebar-blog-categories *cnbSidebarContent></cnb-sidebar-blog-categories>
</cnb-sidebar>
改为
<cnb-sidebar header="分类">
<ng-template #sidebarContent>
<cnb-sidebar-blog-categories></cnb-sidebar-blog-categories>
</ng-template>
</cnb-sidebar>
将
@ContentChild(SidebarContentDirective) content?: SidebarContentDirective;
改为
@ContentChild('sidebarContent') content?: TemplateRef<any>;
将
<div class="sidebar-content" *ngIf="content && content.templateRef && (!items || items.length <= 0)">
<ng-container [ngTemplateOutlet]="content.templateRef"></ng-container>
</div>
改为
<div class="sidebar-content" *ngIf="content && (!items || items.length <= 0)">
<ng-container [ngTemplateOutlet]="content"></ng-container>
</div>
帮顶...................
开头的
– dudu 2周前*
表示给<cnb-sidebar-blog-categories>
外包一个<ng-template>
Unlocking the Power of Angular's @ViewChild and @ContentChild
– dudu 2周前
– dudu 2周前<cnb-posts-sidebar>
是 parent component,<cnb-sidebar>
是 child component,<cnb-sidebar-blog-categories>
是 projected componentAngular @ContentChildren not working with directive as selector
– dudu 2周前