Skip to content

Commit

Permalink
feat(entity): new setMany adapter (#3026) (#3029)
Browse files Browse the repository at this point in the history
Closes #3026
  • Loading branch information
samuelfernandez authored May 26, 2021
1 parent 15bc0df commit a02ea9f
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 1 deletion.
21 changes: 21 additions & 0 deletions modules/entity/spec/sorted_state_adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,27 @@ describe('Sorted State Adapter', () => {
});
});

it('should let you set many entities in the state', () => {
const firstChange = { title: 'First Change' };
const withMany = adapter.setAll([TheGreatGatsby], state);

const withUpserts = adapter.setMany(
[{ ...TheGreatGatsby, ...firstChange }, AClockworkOrange],
withMany
);

expect(withUpserts).toEqual({
ids: [AClockworkOrange.id, TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: {
...TheGreatGatsby,
...firstChange,
},
[AClockworkOrange.id]: AClockworkOrange,
},
});
});

it('should remove existing and add new ones on setAll', () => {
const withOneEntity = adapter.addOne(TheGreatGatsby, state);

Expand Down
21 changes: 21 additions & 0 deletions modules/entity/spec/unsorted_state_adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ describe('Unsorted State Adapter', () => {
});
});

it('should let you set many entities in the state', () => {
const firstChange = { title: 'First Change' };
const withMany = adapter.setAll([TheGreatGatsby], state);

const withUpserts = adapter.setMany(
[{ ...TheGreatGatsby, ...firstChange }, AClockworkOrange],
withMany
);

expect(withUpserts).toEqual({
ids: [TheGreatGatsby.id, AClockworkOrange.id],
entities: {
[TheGreatGatsby.id]: {
...TheGreatGatsby,
...firstChange,
},
[AClockworkOrange.id]: AClockworkOrange,
},
});
});

it('should remove existing and add new ones on setAll', () => {
const withOneEntity = adapter.addOne(TheGreatGatsby, state);

Expand Down
1 change: 1 addition & 0 deletions modules/entity/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface EntityStateAdapter<T> {

setAll<S extends EntityState<T>>(entities: T[], state: S): S;
setOne<S extends EntityState<T>>(entity: T, state: S): S;
setMany<S extends EntityState<T>>(entities: T[], state: S): S;

removeOne<S extends EntityState<T>>(key: string, state: S): S;
removeOne<S extends EntityState<T>>(key: number, state: S): S;
Expand Down
19 changes: 19 additions & 0 deletions modules/entity/src/sorted_state_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,24 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
}
}

function setManyMutably(entities: T[], state: R): DidMutate;
function setManyMutably(entities: any[], state: any): DidMutate {
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: Update<T>, state: R): DidMutate;
function updateOneMutably(update: any, state: any): DidMutate {
return updateManyMutably([update], state);
Expand Down Expand Up @@ -233,6 +251,7 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
upsertOne: createStateOperator(upsertOneMutably),
setAll: createStateOperator(setAllMutably),
setOne: createStateOperator(setOneMutably),
setMany: createStateOperator(setManyMutably),
addMany: createStateOperator(addManyMutably),
updateMany: createStateOperator(updateManyMutably),
upsertMany: createStateOperator(upsertManyMutably),
Expand Down
19 changes: 19 additions & 0 deletions modules/entity/src/unsorted_state_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,24 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
return DidMutate.Both;
}

function setManyMutably(entities: T[], state: R): DidMutate;
function setManyMutably(entities: any[], state: any): DidMutate {
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: T, state: R): DidMutate;
function removeOneMutably(key: any, state: any): DidMutate {
return removeManyMutably([key], state);
Expand Down Expand Up @@ -232,6 +250,7 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
addMany: createStateOperator(addManyMutably),
setAll: createStateOperator(setAllMutably),
setOne: createStateOperator(setOneMutably),
setMany: createStateOperator(setManyMutably),
updateOne: createStateOperator(updateOneMutably),
updateMany: createStateOperator(updateManyMutably),
upsertOne: createStateOperator(upsertOneMutably),
Expand Down
7 changes: 6 additions & 1 deletion projects/ngrx.io/content/guide/entity/adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ state if no changes were made.
- `addMany`: Add multiple entities to the collection.
- `setAll`: Replace current collection with provided collection.
- `setOne`: Add or Replace one entity in the collection.
- `setMany`: Add or Replace multiple entities in the collection.
- `removeOne`: Remove one entity from the collection.
- `removeMany`: Remove multiple entities from the collection, by id or by predicate.
- `removeAll`: Clear entity collection.
Expand All @@ -113,6 +114,7 @@ import { Update, EntityMap, EntityMapOne, Predicate } from '@ngrx/entity';
import { User } from '../models/user.model';

export const loadUsers = createAction('[User/API] Load Users', props<{ users: User[] }>());
export const setUsers = createAction('[User/API] Set Users', props<{ users: User[] }>());
export const addUser = createAction('[User/API] Add User', props<{ user: User }>());
export const setUser = createAction('[User/API] Set User', props<{ user: User }>());
export const upsertUser = createAction('[User/API] Upsert User', props<{ user: User }>());
Expand Down Expand Up @@ -188,6 +190,9 @@ const userReducer = createReducer(
on(UserActions.loadUsers, (state, { users }) => {
return adapter.setAll(users, state);
}),
on(UserActions.setUsers, (state, { users }) => {
return adapter.setMany(users, state);
}),
on(UserActions.clearUsers, state => {
return adapter.removeAll({ ...state, selectedUserId: null });
})
Expand Down Expand Up @@ -242,7 +247,7 @@ type Update<T> = UpdateStr<T> | UpdateNum<T>;

Secondly, `upsertOne` and `upsertMany` will perform an insert or update. If a partial entity is provided this will perform an update.

To prevent partial updates either explicitly set all the fields, setting non-used fields with value `undefined`, or use the `setOne` or `setAll` adapter methods.
To prevent partial updates either explicitly set all the fields, setting non-used fields with value `undefined`, or use the `setOne`, `setAll` or `setMany` adapter methods.

### Entity Selectors

Expand Down

0 comments on commit a02ea9f

Please sign in to comment.