UNPKG

@ngrx/entity

Version:

Common utilities for entity reducers

456 lines (444 loc) 15.9 kB
import { createSelector } from '@ngrx/store'; import { isDevMode } from '@angular/core'; function getInitialEntityState() { return { ids: [], entities: {}, }; } function createInitialStateFactory() { function getInitialState(additionalState = {}) { return Object.assign(getInitialEntityState(), additionalState); } return { getInitialState }; } function createSelectorsFactory() { function getSelectors(selectState) { const selectIds = (state) => state.ids; const selectEntities = (state) => state.entities; const selectAll = createSelector(selectIds, selectEntities, (ids, entities) => ids.map((id) => entities[id])); const selectTotal = createSelector(selectIds, (ids) => ids.length); if (!selectState) { return { selectIds, selectEntities, selectAll, selectTotal, }; } return { selectIds: createSelector(selectState, selectIds), selectEntities: createSelector(selectState, selectEntities), selectAll: createSelector(selectState, selectAll), selectTotal: createSelector(selectState, selectTotal), }; } return { getSelectors }; } var DidMutate; (function (DidMutate) { DidMutate[DidMutate["EntitiesOnly"] = 0] = "EntitiesOnly"; DidMutate[DidMutate["Both"] = 1] = "Both"; DidMutate[DidMutate["None"] = 2] = "None"; })(DidMutate || (DidMutate = {})); function createStateOperator(mutator) { return function operation(arg, state) { const clonedEntityState = { ids: [...state.ids], entities: { ...state.entities }, }; const didMutate = mutator(arg, clonedEntityState); if (didMutate === DidMutate.Both) { return Object.assign({}, state, clonedEntityState); } if (didMutate === DidMutate.EntitiesOnly) { return { ...state, entities: clonedEntityState.entities, }; } return state; }; } function selectIdValue(entity, selectId) { const key = selectId(entity); if (isDevMode() && key === undefined) { console.warn('@ngrx/entity: The entity passed to the `selectId` implementation returned undefined.', 'You should probably provide your own `selectId` implementation.', 'The entity that was passed:', entity, 'The `selectId` implementation:', selectId.toString()); } return key; } function createUnsortedStateAdapter(selectId) { function addOneMutably(entity, state) { const key = selectIdValue(entity, selectId); if (key in state.entities) { return DidMutate.None; } state.ids.push(key); state.entities[key] = entity; return DidMutate.Both; } function addManyMutably(entities, state) { let didMutate = false; for (const entity of entities) { didMutate = addOneMutably(entity, state) !== DidMutate.None || didMutate; } return didMutate ? DidMutate.Both : DidMutate.None; } function setAllMutably(entities, state) { state.ids = []; state.entities = {}; addManyMutably(entities, state); return DidMutate.Both; } function setOneMutably(entity, state) { const key = selectIdValue(entity, selectId); if (key in state.entities) { state.entities[key] = entity; return DidMutate.EntitiesOnly; } state.ids.push(key); state.entities[key] = entity; return DidMutate.Both; } function setManyMutably(entities, state) { const didMutateSetOne = entities.map((entity) => setOneMutably(entity, state)); switch (true) { case didMutateSetOne.some((didMutate) => didMutate === DidMutate.Both): return DidMutate.Both; case didMutateSetOne.some((didMutate) => didMutate === DidMutate.EntitiesOnly): return DidMutate.EntitiesOnly; default: return DidMutate.None; } } function removeOneMutably(key, state) { return removeManyMutably([key], state); } function removeManyMutably(keysOrPredicate, state) { const keys = keysOrPredicate instanceof Array ? keysOrPredicate : state.ids.filter((key) => keysOrPredicate(state.entities[key])); const didMutate = keys .filter((key) => key in state.entities) .map((key) => delete state.entities[key]).length > 0; if (didMutate) { state.ids = state.ids.filter((id) => id in state.entities); } return didMutate ? DidMutate.Both : DidMutate.None; } function removeAll(state) { return Object.assign({}, state, { ids: [], entities: {}, }); } function takeNewKey(keys, update, state) { const original = state.entities[update.id]; const updated = Object.assign({}, original, update.changes); const newKey = selectIdValue(updated, selectId); const hasNewKey = newKey !== update.id; if (hasNewKey) { keys[update.id] = newKey; delete state.entities[update.id]; } state.entities[newKey] = updated; return hasNewKey; } function updateOneMutably(update, state) { return updateManyMutably([update], state); } function updateManyMutably(updates, state) { const newKeys = {}; updates = updates.filter((update) => update.id in state.entities); const didMutateEntities = updates.length > 0; if (didMutateEntities) { const didMutateIds = updates.filter((update) => takeNewKey(newKeys, update, state)).length > 0; if (didMutateIds) { state.ids = state.ids.map((id) => newKeys[id] || id); return DidMutate.Both; } else { return DidMutate.EntitiesOnly; } } return DidMutate.None; } function mapMutably(map, state) { const changes = state.ids.reduce((changes, id) => { const change = map(state.entities[id]); if (change !== state.entities[id]) { changes.push({ id, changes: change }); } return changes; }, []); const updates = changes.filter(({ id }) => id in state.entities); return updateManyMutably(updates, state); } function mapOneMutably({ map, id }, state) { const entity = state.entities[id]; if (!entity) { return DidMutate.None; } const updatedEntity = map(entity); return updateOneMutably({ id: id, changes: updatedEntity, }, state); } function upsertOneMutably(entity, state) { return upsertManyMutably([entity], state); } function upsertManyMutably(entities, state) { const added = []; const updated = []; for (const entity of entities) { const id = selectIdValue(entity, selectId); if (id in state.entities) { updated.push({ id, changes: entity }); } else { added.push(entity); } } const didMutateByUpdated = updateManyMutably(updated, state); const didMutateByAdded = addManyMutably(added, state); switch (true) { case didMutateByAdded === DidMutate.None && didMutateByUpdated === DidMutate.None: return DidMutate.None; case didMutateByAdded === DidMutate.Both || didMutateByUpdated === DidMutate.Both: return DidMutate.Both; default: return DidMutate.EntitiesOnly; } } return { removeAll, addOne: createStateOperator(addOneMutably), addMany: createStateOperator(addManyMutably), setAll: createStateOperator(setAllMutably), setOne: createStateOperator(setOneMutably), setMany: createStateOperator(setManyMutably), updateOne: createStateOperator(updateOneMutably), updateMany: createStateOperator(updateManyMutably), upsertOne: createStateOperator(upsertOneMutably), upsertMany: createStateOperator(upsertManyMutably), removeOne: createStateOperator(removeOneMutably), removeMany: createStateOperator(removeManyMutably), map: createStateOperator(mapMutably), mapOne: createStateOperator(mapOneMutably), }; } function createSortedStateAdapter(selectId, sort) { const { removeOne, removeMany, removeAll } = createUnsortedStateAdapter(selectId); function addOneMutably(entity, state) { return addManyMutably([entity], state); } function addManyMutably(newModels, state) { const models = newModels.filter((model) => !(selectIdValue(model, selectId) in state.entities)); if (models.length === 0) { return DidMutate.None; } else { merge(models, state); return DidMutate.Both; } } function setAllMutably(models, state) { state.entities = {}; state.ids = []; addManyMutably(models, state); return DidMutate.Both; } function setOneMutably(entity, state) { const id = selectIdValue(entity, selectId); if (id in state.entities) { state.ids = state.ids.filter((val) => val !== id); merge([entity], state); return DidMutate.Both; } else { return addOneMutably(entity, state); } } function setManyMutably(entities, state) { const didMutateSetOne = entities.map((entity) => setOneMutably(entity, state)); switch (true) { case didMutateSetOne.some((didMutate) => didMutate === DidMutate.Both): return DidMutate.Both; case didMutateSetOne.some((didMutate) => didMutate === DidMutate.EntitiesOnly): return DidMutate.EntitiesOnly; default: return DidMutate.None; } } function updateOneMutably(update, state) { return updateManyMutably([update], state); } function takeUpdatedModel(models, update, state) { if (!(update.id in state.entities)) { return false; } const original = state.entities[update.id]; const updated = Object.assign({}, original, update.changes); const newKey = selectIdValue(updated, selectId); delete state.entities[update.id]; models.push(updated); return newKey !== update.id; } function updateManyMutably(updates, state) { const models = []; const didMutateIds = updates.filter((update) => takeUpdatedModel(models, update, state)) .length > 0; if (models.length === 0) { return DidMutate.None; } else { const originalIds = state.ids; const updatedIndexes = []; state.ids = state.ids.filter((id, index) => { if (id in state.entities) { return true; } else { updatedIndexes.push(index); return false; } }); merge(models, state); if (!didMutateIds && updatedIndexes.every((i) => state.ids[i] === originalIds[i])) { return DidMutate.EntitiesOnly; } else { return DidMutate.Both; } } } function mapMutably(updatesOrMap, state) { const updates = state.ids.reduce((changes, id) => { const change = updatesOrMap(state.entities[id]); if (change !== state.entities[id]) { changes.push({ id, changes: change }); } return changes; }, []); return updateManyMutably(updates, state); } function mapOneMutably({ map, id }, state) { const entity = state.entities[id]; if (!entity) { return DidMutate.None; } const updatedEntity = map(entity); return updateOneMutably({ id: id, changes: updatedEntity, }, state); } function upsertOneMutably(entity, state) { return upsertManyMutably([entity], state); } function upsertManyMutably(entities, state) { const added = []; const updated = []; for (const entity of entities) { const id = selectIdValue(entity, selectId); if (id in state.entities) { updated.push({ id, changes: entity }); } else { added.push(entity); } } const didMutateByUpdated = updateManyMutably(updated, state); const didMutateByAdded = addManyMutably(added, state); switch (true) { case didMutateByAdded === DidMutate.None && didMutateByUpdated === DidMutate.None: return DidMutate.None; case didMutateByAdded === DidMutate.Both || didMutateByUpdated === DidMutate.Both: return DidMutate.Both; default: return DidMutate.EntitiesOnly; } } function merge(models, state) { models.sort(sort); const ids = []; let i = 0; let j = 0; while (i < models.length && j < state.ids.length) { const model = models[i]; const modelId = selectIdValue(model, selectId); const entityId = state.ids[j]; const entity = state.entities[entityId]; if (sort(model, entity) <= 0) { ids.push(modelId); i++; } else { ids.push(entityId); j++; } } if (i < models.length) { state.ids = ids.concat(models.slice(i).map(selectId)); } else { state.ids = ids.concat(state.ids.slice(j)); } models.forEach((model, i) => { state.entities[selectId(model)] = model; }); } return { removeOne, removeMany, removeAll, addOne: createStateOperator(addOneMutably), updateOne: createStateOperator(updateOneMutably), upsertOne: createStateOperator(upsertOneMutably), setAll: createStateOperator(setAllMutably), setOne: createStateOperator(setOneMutably), setMany: createStateOperator(setManyMutably), addMany: createStateOperator(addManyMutably), updateMany: createStateOperator(updateManyMutably), upsertMany: createStateOperator(upsertManyMutably), map: createStateOperator(mapMutably), mapOne: createStateOperator(mapOneMutably), }; } function createEntityAdapter(options = {}) { const { selectId, sortComparer } = { selectId: options.selectId ?? ((entity) => entity.id), sortComparer: options.sortComparer ?? false, }; const stateFactory = createInitialStateFactory(); const selectorsFactory = createSelectorsFactory(); const stateAdapter = sortComparer ? createSortedStateAdapter(selectId, sortComparer) : createUnsortedStateAdapter(selectId); return { selectId, sortComparer, ...stateFactory, ...selectorsFactory, ...stateAdapter, }; } class Dictionary { } /** * DO NOT EDIT * * This file is automatically generated at build */ /** * Generated bundle index. Do not edit. */ export { Dictionary, createEntityAdapter }; //# sourceMappingURL=ngrx-entity.mjs.map