Skip to content

Commit

Permalink
feat(entity): add 'setOne' method to entity adapter (#2410)
Browse files Browse the repository at this point in the history
Closes #2369
  • Loading branch information
dmytro-gokun authored Feb 27, 2020
1 parent 4dcba7d commit 4b4bb85
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 0 deletions.
2 changes: 2 additions & 0 deletions modules/entity/spec/fixtures/book.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const deepFreeze = require('deep-freeze');
export interface BookModel {
id: string;
title: string;
description?: string;
}

export const AClockworkOrange: BookModel = deepFreeze({
Expand All @@ -18,4 +19,5 @@ export const AnimalFarm: BookModel = deepFreeze({
export const TheGreatGatsby: BookModel = deepFreeze({
id: 'tgg',
title: 'The Great Gatsby',
description: 'A 1925 novel written by American author F. Scott Fitzgerald',
});
32 changes: 32 additions & 0 deletions modules/entity/spec/sorted_state_adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,4 +407,36 @@ describe('Sorted State Adapter', () => {
},
});
});

it('should let you add one entity to the state with setOne()', () => {
const withOneEntity = adapter.setOne(TheGreatGatsby, state);
expect(withOneEntity).toEqual({
ids: [TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: TheGreatGatsby,
},
});
});

it('should let you replace an entity in the state with setOne()', () => {
const withMany = adapter.addOne(
TheGreatGatsby,
adapter.addOne(AnimalFarm, adapter.addOne(AClockworkOrange, state))
);
const updatedBook = {
id: TheGreatGatsby.id,
title: 'A New Hope',
description: undefined,
};

const withUpdates = adapter.setOne(updatedBook, withMany);
expect(withUpdates).toEqual({
ids: [AClockworkOrange.id, updatedBook.id, AnimalFarm.id],
entities: {
[AClockworkOrange.id]: AClockworkOrange,
[updatedBook.id]: updatedBook,
[AnimalFarm.id]: AnimalFarm,
},
});
});
});
27 changes: 27 additions & 0 deletions modules/entity/spec/unsorted_state_adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,31 @@ describe('Unsorted State Adapter', () => {
},
});
});

it('should let you add one entity to the state with setOne()', () => {
const withOneEntity = adapter.setOne(TheGreatGatsby, state);
expect(withOneEntity).toEqual({
ids: [TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: TheGreatGatsby,
},
});
});

it('should let you replace an entity in the state with setOne()', () => {
const withOne = adapter.addOne(TheGreatGatsby, state);
const updatedBook = {
id: TheGreatGatsby.id,
title: 'A New Hope',
description: undefined,
};

const withUpdates = adapter.setOne(updatedBook, withOne);
expect(withUpdates).toEqual({
ids: [TheGreatGatsby.id],
entities: {
[TheGreatGatsby.id]: updatedBook,
},
});
});
});
1 change: 1 addition & 0 deletions modules/entity/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface EntityStateAdapter<T> {
addAll<S extends EntityState<T>>(entities: T[], state: S): S;

setAll<S extends EntityState<T>>(entities: T[], state: S): S;
setOne<S extends EntityState<T>>(entity: 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
13 changes: 13 additions & 0 deletions modules/entity/src/sorted_state_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
return DidMutate.Both;
}

function setOneMutably(entity: T, state: R): DidMutate;
function setOneMutably(entity: any, state: any): DidMutate {
const id = selectIdValue(entity, selectId);
if (id in state.entities) {
state.ids = state.ids.filter((val: string | number) => val !== id);
merge([entity], state);
return DidMutate.Both;
} else {
return addOneMutably(entity, state);
}
}

function updateOneMutably(update: Update<T>, state: R): DidMutate;
function updateOneMutably(update: any, state: any): DidMutate {
return updateManyMutably([update], state);
Expand Down Expand Up @@ -201,6 +213,7 @@ export function createSortedStateAdapter<T>(selectId: any, sort: any): any {
upsertOne: createStateOperator(upsertOneMutably),
addAll: createStateOperator(setAllMutably),
setAll: createStateOperator(setAllMutably),
setOne: createStateOperator(setOneMutably),
addMany: createStateOperator(addManyMutably),
updateMany: createStateOperator(updateManyMutably),
upsertMany: createStateOperator(upsertManyMutably),
Expand Down
16 changes: 16 additions & 0 deletions modules/entity/src/unsorted_state_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,21 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
return DidMutate.Both;
}

function setOneMutably(entity: T, state: R): DidMutate;
function setOneMutably(entity: any, state: any): DidMutate {
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 removeOneMutably(key: T, state: R): DidMutate;
function removeOneMutably(key: any, state: any): DidMutate {
return removeManyMutably([key], state);
Expand Down Expand Up @@ -196,6 +211,7 @@ export function createUnsortedStateAdapter<T>(selectId: IdSelector<T>): any {
addMany: createStateOperator(addManyMutably),
addAll: createStateOperator(setAllMutably),
setAll: createStateOperator(setAllMutably),
setOne: createStateOperator(setOneMutably),
updateOne: createStateOperator(updateOneMutably),
updateMany: createStateOperator(updateManyMutably),
upsertOne: createStateOperator(upsertOneMutably),
Expand Down
5 changes: 5 additions & 0 deletions projects/ngrx.io/content/guide/entity/adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ state if no changes were made.
- `addOne`: Add one entity to the collection
- `addMany`: Add multiple entities to the collection
- `addAll`: Replace current collection with provided collection
- `setOne`: Add or Replace one entity 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 @@ -112,6 +113,7 @@ import { User } from '../models/user.model';

export const loadUsers = createAction('[User/API] Load 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 }>());
export const addUsers = createAction('[User/API] Add Users', props<{ users: User[] }>());
export const upsertUsers = createAction('[User/API] Upsert Users', props<{ users: User[] }>());
Expand Down Expand Up @@ -148,6 +150,9 @@ const userReducer = createReducer(
on(UserActions.addUser, (state, { user }) => {
return adapter.addOne(user, state)
}),
on(UserActions.setUser, (state, { user }) => {
return adapter.setOne(user, state)
}),
on(UserActions.upsertUser, (state, { user }) => {
return adapter.upsertOne(user, state);
}),
Expand Down

0 comments on commit 4b4bb85

Please sign in to comment.