Как нормализовать глубоко вложенные данные с помощью ngrx / entity (EntityState и EntityAdapter)

Я хочу нормализовать данные с моего сервера, чтобы мне было проще использовать их с ngrx / entity.

Мне нравится, как ngrx / entity снижает сложность редукторов и прочего, предоставляя интерфейс EntityState и EntityAdapter. Однако я не считаю, что это хорошо работает с вложенными данными.

У меня 3 уровня данных:

Тренировки -> упражнения -> сеты

Если я использую это с классическим шаблоном ngrx / entity, он быстро становится переполненным, когда я работаю с вложенными данными.

Вот первое, на что я наткнулся при использовании ngrx / entity Вот как данные нормализуются, когда я помещаю все данные о тренировках в После этого я пошарил и добрался до библиотеки normalizr OutputМне нравится, как normalizr нормализует мои данные, а также заменяет значения вложенного массива только идентификатором в качестве ключей к другим объектам (упражнениям, наборам)

Сначала я попробовал объединить несколько состояний сущностей следующим образом: Entity statesНо для этого нужно изменить мой сервер и много логики и усилий.

Я бы хотел как-то объединить normalizr с ngrx / entity .. Получите то же самое, что дает мне normalizr, но у меня есть свобода использовать API-интерфейс entity-адаптера из ngrx / entity, его селекторы и другой код, который находится в моем распоряжении от ngrx / entity

Итак, в конце концов, мой вопрос будет заключаться в том, как нормализовать глубоко вложенные данные с помощью ngrx / entity (как это делает библиотека normalizr) без каких-либо усилий со стороны сервера.


person maran    schedule 09.09.2019    source источник


Ответы (3)


Итак, я нашел обходное решение, все еще используя NGRX.

Прежде чем начать, я просто хочу сказать, что ngrx также имеет пакет ngrx / data, который обеспечивает меньше шаблонов. Но пока я читал об этом, я нашел окончательный ответ на свой вопрос:

https://ngrx.io/guide/data/limitations "Эта библиотека мелкая -клонирует данные сущности в коллекциях. Он не клонирует сложные, вложенные или массивные свойства. Вам придется провести глубокие тесты на равенство и клонировать себя < / strong> перед тем, как запросить у NgRx Data сохранение данных. "

Я считаю, что это верно и для ngrx / entity.

Я начал искать альтернативные решения: BreezeJs, NGXS, Akita, из которых я быстро нашел только NGXS, понятный мне, но потребовавших усилий, чтобы отделить мою реализацию ngrx от проекта.

Итак, я вернулся к ngrx и попытался найти обходной путь для трехуровневых вложенных данных.

Создайте 3 отдельных состояния сущности (я попытаюсь использовать ngrx / data, которые могут точно уменьшить весь шаблон)

Создайте функцию, которая будет возвращать все необходимые сущности и идентификаторы для каждой сущности (используйте 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 и используйте эффект для вызова этого метода и отправьте 3 различных действия для fetchedData ...

Что-то вроде:

   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
    }
}),

Результат:

результат

person maran    schedule 14.09.2019
comment
Хотя для этого требуется чертовски много шаблонного кода - person maran; 14.09.2019
comment
Просто есть запрос, в случае конкретного обучения, если нужно удалить только одно упражнение для определенного обучения, где хранятся эти данные? Мы не можем просто удалить форму упражнения excerciseState. Итак, структура данных массива, содержащая информацию для упражнения, как она извлекается в форме? - person Mr AJ; 27.07.2020
comment
Или нормализатор поддерживает идентификаторы упражнения в parentState? - person Mr AJ; 27.07.2020

@ngrx/entity не предоставляет способов нормализации данных. Вы можете использовать normalizr в сочетании с @ngrx/entity, вы можете, например, нормализовать свои данные в сервисе Angular / NgRx Effect / NgRx reducer.

В документации Redux есть страницы об использовании нормализатора с редукцией по адресу https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape.

person timdeschryver    schedule 09.09.2019
comment
Ох, ладно. Я полагал, что это так, потому что первый корневой уровень данных кажется нормализованным, а вложенный - нет. Как мне смешать вывод normalizr с ngrx / entity? Это не кажется практичным. Создание 3 разных EntityState для каждого объекта ... также адаптер, который делает код более чистым и простым, не работает с данными, отличными от T (entity). Normalizr выводит словарь, который можно назначить непосредственно свойству сущностей EntityState ngrx / entity. Но вам не хватает части идентификаторов для каждого EntityState, кроме корневого. Что normalizr предоставляет. - person maran; 13.09.2019
comment
У вас есть пример такой комбинации normalizr + @ ngrx / entity @timdeschryver? - person maran; 13.09.2019
comment
Нет, извините, я не встречал ни одного - person timdeschryver; 13.09.2019

Я думаю есть 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
);

person maxisam    schedule 15.05.2020