将ng-content传入TemplateRef失效问题排查
问题:Storybook中Angular弹窗组件无法接收ng-content传递的内容
在Storybook环境下尝试从模板传递带选择器的ng-content至弹窗组件,功能既不生效也无报错。
相关代码
story.ts
import { Component, TemplateRef, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MaterialModule } from '@hf-workspace/material'; import { SchemaPopupProps } from '@hf-workspace/prop-types'; import { componentWrapperDecorator, Meta, moduleMetadata, Story } from '@storybook/angular'; import { SchemaPopupSchema } from './popup.schema'; import { SchemaPopupModalComponent } from './schema-popup-modal.component'; @Component({ selector: 'hf-workspace-schema-popup-holder', template: ` <button class="print-btn" mat-raised-button color="basic" (click)="openDialog()">Open Popup</button> <ng-template #customWrapperTemplate> <hf-workspace-schema-popup-modal> <div table>body table is here</div> //not getting </hf-workspace-schema-popup-modal></ng-template >`, }) class SchemaPopupHolderComponent { @ViewChild('customWrapperTemplate') customTemplate: TemplateRef<HTMLAllCollection> | undefined = undefined; popupSchema: SchemaPopupProps; constructor(public dialog: MatDialog) { this.popupSchema = SchemaPopupSchema; } openDialog(): void { this.dialog.open(SchemaPopupModalComponent, { panelClass: 'schema-popup', maxWidth: '80vw', width: '80vw', data: this.popupSchema, }); } } export default { title: 'SchemaPopupModalComponent', component: SchemaPopupModalComponent, decorators: [ moduleMetadata({ imports: [BrowserAnimationsModule, MaterialModule], declarations: [SchemaPopupModalComponent, SchemaPopupHolderComponent], }), componentWrapperDecorator( () => ` <section class="wrapper"> <hf-workspace-schema-popup-holder></hf-workspace-schema-popup-holder> </section>` ), ], } as Meta<SchemaPopupModalComponent>; const Template: Story<SchemaPopupModalComponent> = (args: SchemaPopupModalComponent) => ({ props: args, }); export const Primary = Template.bind({}); Primary.args = {};
子组件HTML
<header *ngIf="data?.header?.required" > <h2>{{data.header.tittle}}</h2> <a class="icon-white-cls-btn" (click)="close()"> <img src="./assets/images/icon-white-cls-btn.svg" /> </a> </header> <mat-dialog-content class="mat-typography" *ngIf="data?.body?.required"> <div class="session-report"> <div> <div *ngFor="let info of data.body?.sessionReport?.info"> <strong>{{info.label}}</strong>{{info.value}} </div> </div> <div> <div *ngFor="let report of data.body?.sessionReport?.report"> <a href="#" [ngClass]="report.icon">{{report.link}}</a> </div> </div> </div> <main *ngIf="data.body?.templates?.required"> <ng-container *ngFor="let temp of data.body?.templates?.tempRef"> <div *ngIf="temp==='table'"> <ng-content select=[table]></ng-content>//not working </div> </ng-container> </main> <p>{{data.header.tittle}}</p> </mat-dialog-content> <mat-dialog-actions align="end" *ngIf="data?.footer?.required"> <ng-container *ngFor="let button of data.footer.buttons"> <ng-container [ngSwitch]="button"> <button *ngSwitchCase="'CANCEL'" mat-button mat-dialog-close>CANCEL</button> <button *ngSwitchCase="'APPROVE'" mat-button [mat-dialog-close]="true" cdkFocusInitial>APPROVE</button> </ng-container> </ng-container> </mat-dialog-actions>
问题原因
MatDialog打开组件时,会直接创建全新的组件实例并挂载到全局Overlay容器中,而非使用你在customWrapperTemplate里定义的<hf-workspace-schema-popup-modal>实例。你写的带[table]属性的投影内容根本没被关联到弹窗实例上,自然无法通过ng-content渲染。
解决方案
通过模板引用(TemplateRef)将需要投影的内容传递给弹窗,在弹窗组件中使用ngTemplateOutlet渲染。
1. 修改story.ts中的弹窗打开逻辑
@Component({ selector: 'hf-workspace-schema-popup-holder', template: ` <button class="print-btn" mat-raised-button color="basic" (click)="openDialog()">Open Popup</button> <!-- 单独定义需要传递的模板 --> <ng-template #tableTemplate> <div table>body table is here</div> </ng-template> `, }) class SchemaPopupHolderComponent { @ViewChild('tableTemplate') tableTemplate: TemplateRef<any> | undefined; popupSchema: SchemaPopupProps; constructor(public dialog: MatDialog) { this.popupSchema = SchemaPopupSchema; } openDialog(): void { this.dialog.open(SchemaPopupModalComponent, { panelClass: 'schema-popup', maxWidth: '80vw', width: '80vw', data: { ...this.popupSchema, // 将模板传递到弹窗的data中 templates: { ...this.popupSchema.templates, tableTemplate: this.tableTemplate } }, }); } }
2. 修改子组件HTML,替换ng-content为模板渲染
<main *ngIf="data?.body?.templates?.required"> <ng-container *ngFor="let temp of data.body?.templates?.tempRef"> <div *ngIf="temp==='table'"> <!-- 使用ngTemplateOutlet渲染传入的模板 --> <ng-container *ngTemplateOutlet="data.body.templates.tableTemplate"></ng-container> </div> </ng-container> </main>
补充说明
- 若要保留
ng-content的使用方式,可将整个弹窗组件包装在模板中,通过MatDialog.open的template参数传入,但这种方式灵活性较低,不如模板引用方案实用。 - 确保
SchemaPopupProps类型定义中包含templates.tableTemplate字段,避免类型报错。
内容的提问来源于stack exchange,提问作者3gwebtrain
相关产品推荐
相关产品推荐

