如何使用ngrx / entity(EntityState和EntityAdapter)规范深度嵌套的数据

时间:2019-09-09 14:31:59

标签: normalization ngrx angular8 state-management ngrx-entity

我希望规范服务器中的数据,以便可以更方便地通过ngrx / entity使用它。

我喜欢ngrx / entity如何通过提供EntityState接口和EntityAdapter来减少化简器和其他内容的复杂性。但是我发现它不适用于嵌套数据。

我有3个级别的数据:

培训->练习->设置

如果我将其与经典的ngrx / entity模式一起使用,则在处理嵌套数据时会很快变得拥挤。

以下是使用ngrx / entity时遇到的第一件事 This is how data gets normalized when I put all trainings data in 之后我偷偷摸摸地走到了normalizr库 Output 我喜欢normalizr如何规范化我的数据,以及如何使用仅id作为其他实体(练习,集合)的键替换嵌套数组值

我首先尝试的是结合多个实体状态,如下所示: Entity states 但这需要更改我的服务器以及大量的逻辑和精力。

我想要以某种方式将normalizr与ngrx / entity结合。.获得normalizr给我的相同的东西,但可以自由地使用ngrx / entity的实体适配器api,它是选择器和其他代码,可从ngrx获得我的服务/实体

因此,最重要的是,我的问题是如何在无需某种服务器工作的情况下使用ngrx / entity标准化深层嵌套数据(就像normalizr库一样)。

3 个答案:

答案 0 :(得分:1)

@ngrx/entity不提供标准化数据的方法。您可以将normalizr与@ngrx/entity结合使用,例如,可以在Angular服务/ NgRx效果/ NgRx归约器中对数据进行归一化。

Redux文档在https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape上有一些有关将规范化器与redux一起使用的页面。

答案 1 :(得分:0)

所以我在仍然使用NGRX的同时找到了一些解决方法

在我开始之前,我只想说ngrx还有ngrx / data pack,它提供了更少的样板。但是,当我阅读有关它的内容时,我找到了一个明确的答案:

https://ngrx.io/guide/data/limitations “此库浅克隆集合中的实体数据。它不克隆复杂的,嵌套的或数组属性。您必须进行深度相等性测试并克隆自己,然后要求NgRx数据保存数据。”

我相信ngrx / entity也是这样。

我开始寻找替代解决方案:BreezeJs,NGXS,秋田,从中我才发现NGXS对我来说是可以快速理解的,但是需要付出一些努力才能将我的ngrx实现与项目分离。

所以我回到ngrx并尝试针对3个级别的深层嵌套数据做一个解决方法

创建3个独立的实体状态(我将尝试使用ngrx / data来切实减少所有样板)

创建一个函数,该函数将返回每个实体的所有必要实体和ID(使用normalizr进行标准化)

export function normalizeTrainingArray(trainings: Training[]) {
var normalized = normalize(trainings, trainingsSchema);

var entities = {
    trainings: {},
    exercises: {},
    sets: {}
}
entities.trainings = normalized.entities.trainings ? normalized.entities.trainings : {};
entities.exercises = normalized.entities.exercises ? normalized.entities.exercises : {};
entities.sets = normalized.entities.sets ? normalized.entities.sets : {};

var ids = {
    trainingIds: [],
    exerciseIds: [],
    setIds: []
}
ids.trainingIds = normalized.entities.trainings ? Object.values(normalized.entities.trainings).map(x => x.id) : [];
ids.exerciseIds = normalized.entities.exercises ? Object.values(normalized.entities.exercises).map(x => x.id) : [];
ids.setIds = normalized.entities.sets ? Object.values(normalized.entities.sets).map(x => x.id) : [];

return {
    entities,
    ids
}

像这样的东西就足够了。 发送normalizeData操作并使用effect调用此方法,并为fetchedData调度3个不同的操作...

类似的东西:

   trainingsNormalized$ = createEffect(() =>
    this.actions$.pipe(
        ofType(TrainingActions.normalizeTrainings),
        tap(payload => {

            var normalized = normalizeTrainingArray(payload.trainings);

            this.store.dispatch(TrainingActions.trainingsFetched({ entities: normalized.entities.trainings, ids: normalized.ids.trainingIds }))
            this.store.dispatch(ExerciseActions.exercisesFetched({ entities: normalized.entities.exercises, ids: normalized.ids.exerciseIds }))
            this.store.dispatch(SetActions.setsFetched({ entities: normalized.entities.sets, ids: normalized.ids.setIds }))
        })
    )
    , { dispatch: false });

在一个样品减压器中:

    // GET ALL
on(TrainingActions.trainingsFetched, (state: TrainingState, payload: { entities: Dictionary<Training>, ids: string[] }) => {
    return {
        ...state,
        entities: payload.entities,
        ids: payload.ids
    }
}),

结果是:

result

答案 2 :(得分:0)

我认为有2种解决方案

  1. 使用https://github.com/paularmstrong/normalizr规范化数据

  2. 使用https://ngrx.io/guide/entity/adapter#adapter-collection-methods映射功能进行深度克隆。

return adapter.map(
  entity => entity.id == obj.id ? {...entity, foo: 'bar'} : entity, 
  state
);

或者您可以使用https://immerjs.github.io/immer/docs/introduction来处理

return adapter.map(
  entity => entity.id == obj.id ? produce(entity, (draft: any) => { draft.foo = 'bar' }) : entity, 
  state
);