Skip to content

Commit

Permalink
feat(entity): add 'setOne' method ngrx#2369
Browse files Browse the repository at this point in the history
  • Loading branch information
dmytro-gokun committed Feb 24, 2020
1 parent a0a336a commit f469147
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 0 deletions.
13 changes: 13 additions & 0 deletions docs/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
- ~~`addAll`~~: (Deprecated and renamed to `setAll`). ~~Replace current collection with provided collection~~
- `setAll`: Replace current collection with provided collection
- `setOne`: Add or Replace on entity in the collection
- `removeOne`: Remove one entity from the collection
- `removeMany`: Remove multiple entities from the collection
- `removeAll`: Clear entity collection
Expand Down Expand Up @@ -117,6 +118,7 @@ import { User } from './user.model';
export enum UserActionTypes {
LOAD_USERS = '[User] Load Users',
ADD_USER = '[User] Add User',
SET_USER = '[User] Set User',
UPSERT_USER = '[User] Upsert User',
ADD_USERS = '[User] Add Users',
UPSERT_USERS = '[User] Upsert Users',
Expand All @@ -139,6 +141,12 @@ export class AddUser implements Action {
constructor(public payload: { user: User }) {}
}

export class SetUser implements Action {
readonly type = UserActionTypes.SET_USER;

constructor(public payload: { user: User }) {}
}

export class UpsertUser implements Action {
readonly type = UserActionTypes.UPSERT_USER;

Expand Down Expand Up @@ -188,6 +196,7 @@ export class ClearUsers implements Action {
export type UserActionsUnion =
| LoadUsers
| AddUser
| SetUser
| UpsertUser
| AddUsers
| UpsertUsers
Expand Down Expand Up @@ -223,6 +232,10 @@ export function reducer(state = initialState, action: UserActionsUnion): State {
return adapter.addOne(action.payload.user, state);
}

case UserActionTypes.SET_USER: {
return adapter.setOne(action.payload.user, state);
}

case UserActionTypes.UPSERT_USER: {
return adapter.upsertOne(action.payload.user, state);
}
Expand Down
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 withOne = 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, withOne);
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 => 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

0 comments on commit f469147

Please sign in to comment.