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

Angular Storybook中ngTemplateOutlet模板无法访问item上下文问题

问题

在为设计系统的Select组件编写Storybook文档时,通过[itemTemplate]="itemTestTemplate"传入自定义模板后,模板无法访问上下文里的item变量。相关代码如下:

组件TS代码

import {Component, EventEmitter, Input, Output, TemplateRef} from '@angular/core';
 
@Component({
  selector: 'wgw-select',
  templateUrl: './select.component.html',
  styleUrls: ['./select.component.scss']
})
export class SelectComponent<T extends {id: string | number}> {
 
  @Input() itemTemplate!: TemplateRef<any>;
  @Input() items: T[];
  @Input() selectedItem: T;
  @Output() selectedItemChange: EventEmitter<T> = new EventEmitter<T>();
 
  selectValue(newValue: T) {
    this.selectedItem = newValue;
    this.selectedItemChange.emit(this.selectedItem);
  }
 
  equals(item1: T, item2: T) {
    return item1?.id === item2?.id
  }
}

组件HTML模板代码

<div class="dropdown">
  <button class="fs-4" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
    <ng-container *ngTemplateOutlet="this.itemTemplate; context: {item: selectedItem}"></ng-container>
    <wgw-icon name="chevron-down" class="ms-4"></wgw-icon>
  </button>
  <div class="dropdown-menu mt-5" aria-labelledby="dropdownMenuButton1">
    <div class="dropdown-item fs-4" *ngFor="let item of items" (click)="selectValue(item)" [class.selected]="equals(selectedItem, item)">
      <wgw-icon [name]="'check'" *ngIf="equals(selectedItem, item)" class="me-4"></wgw-icon>
      <ng-container *ngTemplateOutlet="this.itemTemplate; context: {item: item}"></ng-container>
    </div>
  </div>
</div>

已编写的Story代码

import {SelectComponent} from '@design-system/atom/select/select.component';
import {moduleMetadata, Story} from '@storybook/angular';
import {IconComponent} from '@design-system/atom/icon/icon.component';
 
export default {
  title: 'Atom/Select',
  component: SelectComponent,
  args: {
 
  },
  decorators: [
    moduleMetadata({
      declarations: [IconComponent]
    }),
  ],
}
 
const Template: Story<SelectComponent<any>> = (args) => ({
  props: args,
  template: `
    <div>
        <ng-template #itemTestTemplate let-item="item">
          {{ item?.name }}
        </ng-template>
        <wgw-select [items]="items" [itemTemplate]="itemTestTemplate"></wgw-select>
    </div>
  `
})
 
export const Select = Template.bind({});
Select.args = {
  items: [
    {id: 0, name: 'item1'},
    {id: 1, name: 'item2'},
    {id: 2, name: 'item3'}
  ],
  selectedItem: {id: 0, name: 'item1'}
}
原因分析

问题核心在于两个点:

  1. Angular模板变量接收语法错误:let-item="item"的写法会让Angular尝试从模板自身的上下文中查找变量,而非使用*ngTemplateOutlet传入的上下文值。
  2. 组件TS中items和selectedItem未初始化,可能导致初始渲染时出现undefined,进一步引发模板渲染异常。
修复方案

1. 修正Story中的模板变量接收语法

将Story模板里的let-item="item"改为let-item,Angular会自动匹配传入上下文里的同名键值:

const Template: Story<SelectComponent<any>> = (args) => ({
  props: args,
  template: `
    <div>
        <ng-template #itemTestTemplate let-item>
          {{ item?.name }}
        </ng-template>
        <wgw-select [items]="items" [itemTemplate]="itemTestTemplate" [(selectedItem)]="selectedItem"></wgw-select>
    </div>
  `
})

2. 初始化组件TS中的输入属性

为避免初始渲染时的undefined问题,给items和selectedItem设置初始值:

export class SelectComponent<T extends {id: string | number}> {

  @Input() itemTemplate!: TemplateRef<any>;
  @Input() items: T[] = []; // 初始化空数组
  @Input() selectedItem!: T; // 若允许未选中状态,可改为`selectedItem?: T`
  @Output() selectedItemChange: EventEmitter<T> = new EventEmitter<T>();

  // ... 其他代码保持不变
}

3. 优化组件HTML模板(可选)

模板中可以省略this.直接引用组件属性,让代码更简洁规范:

<div class="dropdown">
  <button class="fs-4" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
    <ng-container *ngTemplateOutlet="itemTemplate; context: {item: selectedItem}"></ng-container>
    <wgw-icon name="chevron-down" class="ms-4"></wgw-icon>
  </button>
  <div class="dropdown-menu mt-5" aria-labelledby="dropdownMenuButton1">
    <div class="dropdown-item fs-4" *ngFor="let item of items" (click)="selectValue(item)" [class.selected]="equals(selectedItem, item)">
      <wgw-icon [name]="'check'" *ngIf="equals(selectedItem, item)" class="me-4"></wgw-icon>
      <ng-container *ngTemplateOutlet="itemTemplate; context: {item: item}"></ng-container>
    </div>
  </div>
</div>

完成以上修改后,模板就能正确访问到传入的item变量,组件也能正常初始化和工作。

内容的提问来源于stack exchange,提问作者Benoit Bonavia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 12:45:25