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

Angular Nx项目NgRx逻辑正常但Storybook无法显示bookmark

Angular Nx项目NgRx管理的Bookmark数据无法在Storybook渲染问题定位

核心问题原因

  • Storybook运行环境未正确加载依赖
    Storybook是独立的组件运行沙箱,不会自动继承主应用或业务库的全局模块配置。你当前的Story配置没有导入NgRx的Store模块、Effects模块、HttpClient模块、浏览器动画模块等必需依赖,也没有正确注册Bookmark对应的reducer和effect,组件实例化时要么注入Store失败,要么dispatch的获取数据action无法被effect处理,根本不会触发数据加载流程。另外你的LibModule中exports数组为空,即使外部导入该模块也无法使用BookmarkListComponent。
  • 代码存在复制遗留的拼写与命名错误
    1. Action枚举中DELETE_BOOKMARK_SUCCESS的字符串值写为'DELETE_BOOMARK_SUCCESS',BOOKMARK误写为BOOMARK,会导致删除成功的action无法匹配reducer对应分支
    2. Effect文件中服务名、action导入别名、effect流名称全部保留了之前Todo示例的命名(todoService、fromTodoActions、deleteTodo$),虽然TS编译不会报错,但提高了隐性bug概率
  • 组件代码存在缺失与逻辑漏洞
    1. 组件中声明bookmarks$: Observable<Bookmark[]>但未从rxjs导入Observable类型,Storybook的严格编译环境下可能导致变量初始化异常
    2. 模板中空状态判断绑定的state变量无任何赋值逻辑,初始固定为false,即使无数据也不会触发空提示
    3. 组件冗余注入了BookmarksService,实际全量使用NgRx流程,该服务未被调用
  • 接口环境依赖导致Storybook请求失败
    服务中接口地址硬编码为本地json-server地址http://localhost:3000/bookmarks/,如果启动Storybook时json-server未启动、端口被占用或存在跨域限制,HTTP请求会直接失败,且组件未做错误捕获提示,最终表现为列表无数据。

修复步骤

  • 修复模块导出配置:在LibModule的exports数组中添加BookmarkListComponent,允许外部模块(包括Storybook)引用该组件
@NgModule({
  // ...其他配置
  exports: [BookmarkListComponent]
})
export class LibModule {}
  • 修正Action枚举拼写错误,统一代码命名:
export enum BookmarkActionsTypes {
  // ...其他action
  DELETE_BOOKMARK_SUCCESS = 'DELETE_BOOKMARK_SUCCESS', // 修正BOOMARK的拼写错误
  // ...其他action
}

同步将Effect中所有Todo相关的命名替换为Bookmark对应命名,避免混淆。

  • 补全组件缺失导入,清理冗余代码:在组件顶部添加Observable导入,删除未使用的BookmarksService注入,补充空状态判断逻辑
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
// ...其他导入
export class BookmarkListComponent{
  bookmarks$ : Observable<Bookmark[]>;
  noData$: Observable<boolean>;
  // ...
  constructor(private _snackBar: MatSnackBar, private store:Store) {
    this.bookmarks$ = this.store.pipe(select(fromBookmarkSelectors.selectAll))
    this.noData$ = this.bookmarks$.pipe(map(items => items.length === 0))
    this.store.dispatch(new fromBookmarkActions.GetBookmarks());
  }
}
  • 配置Story的全局依赖:编写BookmarkList组件的story时,在moduleMetadata装饰器中导入所有必需模块,推荐优先使用mock服务替代真实接口调用,避免依赖本地json-server环境
// bookmark-list.component.stories.ts
import { moduleMetadata, Story, Meta } from '@storybook/angular';
import { BookmarkListComponent } from './bookmark-list.component';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { BookmarkReducer } from '../../store/reducers';
import { BookmarksEffects } from '../../store/effects';
import { BookmarksService } from '../../services/bookmarks-service.service';
import { of } from 'rxjs';

export default {
  title: 'Bookmark/List',
  component: BookmarkListComponent,
  decorators: [
    moduleMetadata({
      imports: [
        BrowserAnimationsModule,
        HttpClientModule,
        MatListModule,
        MatIconModule,
        MatButtonModule,
        MatSnackBarModule,
        StoreModule.forRoot({ angular: BookmarkReducer }),
        EffectsModule.forRoot([BookmarksEffects])
      ],
      providers: [
        // 使用mock服务替代真实接口,不依赖本地json-server
        {
          provide: BookmarksService,
          useValue: {
            getBookmarks: () => of([
              {id:123456, name:"Zeke", code:"93ac3Walk3r"},
              {id:242668, name:"Rex", code:"ShadowMoses"},
              {id:849761, name:"Ray", code:"Tanker"}
            ]),
            deleteBookmark: () => of(null)
          }
        }
      ]
    })
  ]
} as Meta<BookmarkListComponent>;

const Template: Story<BookmarkListComponent> = (args: BookmarkListComponent) => ({
  props: args,
});

export const Default = Template.bind({});
Default.args = {
  Color: 'black',
  backgroundColor: '#ffffff'
};
  • 调整reducer删除逻辑:将DELETE_BOOKMARK分支的removeOne操作移到DELETE_BOOKMARK_SUCCESS分支中,避免接口请求失败时前端提前删除数据导致状态不一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 20:51:20