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

如何在Storybook中为Angular组件添加宿主级Provider?

在Storybook中为Angular组件添加宿主级别Provider

问题背景

我们有两个Angular组件,ChildCmp通过@Optional() @Host()装饰器注入ParentCmp,这意味着它只会在自身的宿主元素层级查找该依赖:

@Component({
  selector: "child-cmp",
  template: "<p>Hello</p>",
})
class ChildCmp {
  constructor(@Optional() @Host() parentCmp: ParentCmp) {}
}

@Component({
  selector: "parent-cmp",
  template: "<child-cmp></child-cmp>",
})
class ParentCmp {}

在Angular单元测试中,我们可以通过TestBed.overrideComponent给ChildCmp添加宿主级Provider:

testBed.overrideComponent(ChildCmp, {
  add: {
    providers: [{ provide: ParentCmp, useValue: {} }],
  },
});

但在Storybook中,常规通过moduleMetadata添加的是模块级Provider,受限于@Host()的查找规则,这种方式无法被ChildCmp识别到:

const Template: Story<ChildCmp> = (args: ChildCmp) => ({
  props: args,
  template: `<child-cmp></child-cmp>`,
  moduleMetadata: {
    providers: [
      {
        provide: ParentCmp,
        useValue: {},
      },
    ],
  },
});

export const ChildCmp = Template.bind({});

解决方案

方法1:使用componentOverrides覆盖组件元数据

Storybook支持通过componentOverrides配置项,直接修改组件的元数据,效果和测试中的overrideComponent一致,能给组件添加宿主级Provider:

import { Meta, Story } from '@storybook/angular';
import { ChildCmp, ParentCmp } from './path-to-components';

const Template: Story<ChildCmp> = (args: ChildCmp) => ({
  props: args,
  template: `<child-cmp></child-cmp>`,
  componentOverrides: {
    ChildCmp: {
      providers: [{ provide: ParentCmp, useValue: {} }]
    }
  }
});

export const Default = Template.bind({});
Default.parameters = {
  component: ChildCmp
};

方法2:创建临时宿主包装组件

如果你的Storybook版本不支持componentOverrides,可以手动创建一个临时的宿主组件,在该组件的providers中声明ParentCmp,再将ChildCmp嵌套在其中:

import { Component } from '@angular/core';
import { Meta, Story } from '@storybook/angular';
import { ChildCmp, ParentCmp } from './path-to-components';

// 临时宿主包装组件
@Component({
  selector: 'host-wrapper',
  template: '<child-cmp></child-cmp>',
  providers: [{ provide: ParentCmp, useValue: {} }]
})
class HostWrapperComponent {}

const Template: Story<ChildCmp> = (args: ChildCmp) => ({
  props: args,
  template: `<host-wrapper></host-wrapper>`,
  declarations: [HostWrapperComponent]
});

export const Default = Template.bind({});

这两种方式都能让ChildCmp通过@Host()装饰器获取到对应的ParentCmp依赖,符合需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 00:50:23