NgRx中派生ItemVM ViewModel时如何确保依赖数据已加载?
NgRx视图模型派生:确保数据加载完成的解决方案
核心概念澄清
你创建的selectItemViewModel是一个memoized选择器函数,本身并非Observable,但当你在组件中通过store.select(selectItemViewModel)调用它时,会返回一个Observable<ItemVM[]>。这个Observable会自动监听Store中users、categories、items的状态变化,只有当依赖的状态更新时,才会重新计算并发出新的派生视图模型。
解决数据未加载完成的问题
1. 先修复Selector的潜在崩溃风险
当前的Selector中,users.find()或categories.find()可能返回undefined(比如数据未加载完成时),直接访问.name会抛出运行时错误。先给映射逻辑添加空值保护:
export const selectItemViewModel = createSelector( selectUsers, selectCategories, selectItems, (users, categories, items) => { return items.map(i => ({ id: i.id, userName: users.find(u => u.id === i.userId)?.name || '未知用户', categoryName: categories.find(c => c.id === i.categoryId)?.name || '未知分类' } as ItemVM)); } );
2. 确保所有数据加载完成后再渲染
有两种常用方案控制渲染时机:
方案一:通过Store维护加载状态
在对应的State结构中添加加载完成标记(如loaded布尔值),并创建对应的选择器:
// 示例State结构 interface AppState { users: { list: User[], loaded: boolean }; categories: { list: Category[], loaded: boolean }; items: { list: Item[], loaded: boolean }; } // 对应加载状态的选择器 export const selectUsersLoaded = createSelector( selectUsersState, state => state.loaded ); export const selectCategoriesLoaded = createSelector( selectCategoriesState, state => state.loaded ); export const selectItemsLoaded = createSelector( selectItemsState, state => state.loaded ); // 组合判断所有数据是否加载完成 export const selectAllDataLoaded = createSelector( selectUsersLoaded, selectCategoriesLoaded, selectItemsLoaded, (usersLoaded, categoriesLoaded, itemsLoaded) => usersLoaded && categoriesLoaded && itemsLoaded );
然后在组件中合并Observable,等待全部数据加载完成后再处理视图模型:
import { combineLatest } from 'rxjs'; // 组件类中 itemVMs: ItemVM[] = []; isLoading = true; ngOnInit() { combineLatest([ this.store.select(selectItemViewModel), this.store.select(selectAllDataLoaded) ]).subscribe(([vms, allLoaded]) => { this.isLoading = !allLoaded; if (allLoaded) { this.itemVMs = vms; } }); }
模板中根据状态切换显示:
<div *ngIf="isLoading">加载中...</div> <ul *ngIf="!isLoading"> <li *ngFor="let vm of itemVMs"> ID: {{vm.id}} | 用户: {{vm.userName}} | 分类: {{vm.categoryName}} </li> </ul>
方案二:在组件中过滤Observable
如果没有维护加载状态,也可以通过过滤操作,等待三个数据源都返回有效数据:
import { filter } from 'rxjs/operators'; // 组件类中 ngOnInit() { this.store.select(selectItemViewModel).pipe( filter(vms => { // 间接判断依赖数据是否加载完成 return vms.length > 0 && vms.every(vm => vm.userName !== '未知用户' && vm.categoryName !== '未知分类'); }) ).subscribe(vms => { this.itemVMs = vms; this.isLoading = false; }); }
关键总结
- NgRx选择器本身不是Observable,但通过
store.select()调用后会返回响应式的Observable,自动追踪状态变化。 - 必须处理数据未加载时的空值情况,避免运行时错误。
- 通过维护加载状态或过滤Observable,可以确保只有在所有依赖数据就绪后,才渲染派生的视图模型。
内容的提问来源于stack exchange,提问作者Lou
相关产品推荐
相关产品推荐

