So I found some workaround solution while still using NGRX
Before I start I just want to say that ngrx also has ngrx/data pack which provides less boilerplate. But while I was reading about it I found a definitive answer to my question:
https://ngrx.io/guide/data/limitations
"This library shallow-clones the entity data in the collections. It doesn't clone complex, nested, or array properties. You'll have to do the deep equality tests and cloning yourself before asking NgRx Data to save data."
I believe this is also true for ngrx/entity.
I started to look for alternative solutions: BreezeJs, NGXS, Akita from which I only found NGXS understandable to me fast but would require effort to detach my ngrx implementation from project.
So I got back to ngrx and tried to do a workaround for 3 levels deep nested data
Create 3 separate entity states ( I'll try to use ngrx/data that could certanly reduce all the boilerplate)
Create a function that will return all necessary entities and ids for each entity (use normalizr for normalization)
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
}
Something like this will suffice.
Send normalizeData action and use effect to call this method and dispatch 3 different actions for fetchedData...
Something along the lines of:
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 });
And in one sample reducer:
// GET ALL
on(TrainingActions.trainingsFetched, (state: TrainingState, payload: { entities: Dictionary<Training>, ids: string[] }) => {
return {
...state,
entities: payload.entities,
ids: payload.ids
}
}),
Result is:
