ngrx:规范化结构与可观察实体

Jor*_*rdi 3 typescript ngrx angular

我试图弄清楚如何在我的ngrx商店中使用“实体数组”进行处理。

我的意思是,我PlanDTO从api服务器上获取了我的收藏。因此,根据我阅读的一些文档,我需要创建某种“表”来存储它们:

export interface IPlan {
    id?: string;
    name?: string;
    ...
}

export interface IPlanRedux {
    byId: { [key: string]: IPlan };
    allIds: Array<string>;
}
Run Code Online (Sandbox Code Playgroud)

因此,在'LOAD_PLANS'调度动作时,也会启动效果:

@Injectable()
export class PlanEffects {
  constructor(
    private actions$: Actions,
    private store$: Store<IStore>,
    private planService: PlansService,
  ) { }

  @Effect({ dispatch: true })
  loadPlans$: Observable<Action> = this.actions$
    .ofType('LOAD_PLANS')
    .switchMap((action: Action) =>
      this.planService.list()
        .map((plans: Array<PlanDTO>) => {
          return { type: 'LOAD_PLANS_SUCCESS', payload: plans };
        })
        .catch((err: ApiError) => {
          return Observable.of({ type: 'LOAD_PLANS_FAILED', payload: { code: err.code, msg: err.message } });
        })
    );
}
Run Code Online (Sandbox Code Playgroud)

如您所见,如果一切都很好,则将“分派”另一个操作:'LOAD_PLANS_SUCCESS'。因此,此操作的简化器是:

private static loadPlansSuccess(plansRdx: IPlanRedux, type, payload: Array<PlanDTO>) {
    const plans = payload;
    const newPlans = plans.filter(plan => !plansRdx.byId[plan.id]);

    const newPlanIds = newPlans.map(plan => plan.id);
    const newPlanEntities = plans.reduce((entities: { [id: string]: IPlan }, plan: IPlan) => {
        return Object.assign(entities, {
            [plan.id]: plan
        });
    },
    {});

    return {
        ids: [ ...plansRdx.allIds, ...newPlanIds ],
        byId: Object.assign({}, plansRdx.byId, newPlanEntities),
    };
}
Run Code Online (Sandbox Code Playgroud)

一切似乎都正常,但是,我不太清楚。我认为这里需要了解两个概念。

  1. 内部表以存储redux结构。
  2. 前端组件。

我主要的头痛是我需要在组件中使用Observable<IPlanRedux>而不是an 来处理Observable<IPlan>。我的意思是,在我的组件中,我想使用IPlans代替带有field allIds或的表ById

因此,在内部,我要照顾我的IPlanRedux桌子,但另一方面,我想“发出”已删除的计划和已添加的计划。

我不知道我对ID的解释很好。

编辑

我尝试使用选择器:

export interface IPlanRedux {
    entities: { [key: string]: IPlan };
    ids: Array<string>;
}

export const getPlans = (planRdx: IPlanRedux) => planRdx.entities;

const getPlansState = (state: IStore) => state.plans;
export const getPlanEntities = createSelector(getPlansState, getPlans);
Run Code Online (Sandbox Code Playgroud)

进入我的组件:

this.plans$ = this.store$.select(fromRoot.getPlanEntities)
    .map(plan => {
    if (plan.id != null)
Run Code Online (Sandbox Code Playgroud)

ols*_*lsn 5

首先:“规则和概念”只是应该帮助您找到适合您的应用程序的解决方案的指南-我不知道有人能绝对实现所有这些规则。每个应用程序都是个别的,没有“一刀切”的解决方案。

因此,仅因为有人告诉您使用类似表的结构,并不意味着这是解决您问题的最佳解决方案(另一方面,这也不意味着这将是一个糟糕的解决方案)-您是唯一知道数据和要求的人,您可能是唯一能够做出判断的人-因此,不要盲目相信某些规则,并希望不会有任何不利之处。


至于您的问题:

1.内部表为了存储redux结构。

redux-store或ngrx-store通常与不可变对象结合使用(这使更改检测的性能更好)-在这种情况下,以平面结构而不是嵌套结构管理数据要容易得多,因为在平面(表)结构中,您最多只需重新创建2个级别的数据。但是,当对象具有深层嵌套时,在执行数据突变时必须重新创建更多层数据(取决于嵌套深度)。

因此:当您不使用不可变对象时,您将不会真正从一个或另一个数据结构中受益。(我在这里没有提出任何建议,这是一个技术决定,具体取决于您,个人而言,我喜欢使用不可变对象,但我并非以此为出发点)

2.前端组件

同样,这里有一些“规则”,例如“智能和哑巴组件”,“选择语句与让函数”等。-再说一次:没有真正完美的,万能的解决方案。

至于Observable<IPlanRedux>-您不必到处做-如果您不满意let函数,则可以在其中创建一些服务类并实现一些简单的select语句:

class StoreService {
    iPlans$ = this.store.select(state => state.planRedux)
        .map(planRedux => planRedux.allIds.map(id => planRedux.byId[id]));
}
Run Code Online (Sandbox Code Playgroud)

一些个人建议,具体取决于您的情况:

如果您是出于兴趣和学习目的来做这个项目,请:发疯,尽可能多地尝试并尽可能多地学习,尽可能多地休息。-您遇到挫折,在学习需要进行重构,但这是值得的。NGRX和redux模式通常是一个非常不错的工具,但是很难缠住您的头,特别是如果您习惯于其他模式。

如果您是为客户进行此项目并作为可用于生产的应用程序:坚持使用您已经知道或接受的事实,如果这是您的第一个ngrx和/或angular2项目,那么在质量和时间上将不可避免地产生后果。 (除非你是个疯狂的天才;-)。