You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:05:26