Search code examples
angularngrxngrx-storengrx-effectsngrx-entity

How to use NgRx entity updateOne or updateMany with effects?


I'm new to NgRx entities... And don't know how to take advantage of it in order to update my state. I just like to satisfy my action right in my effect.

This is my effect:

updateItem$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ItemsActions.updateItem),
      concatMap(({ url, item }) => {
        return this._itemsProxy.updateItem(url, item).pipe(
          map((item: { id:number; name: string; }) => {

            // I get an error that says:
            // Type '{ id: number; name: string; }' is not assignable to type 'Update<{ id:number, name: string }>'
            // So how should I satisfy my action?

            return ItemsActions.updateItemSuccess({ item })
          }),
          catchError((error) => of(ItemsActions.failure({ error }))),
        );
      }),
    ),
  );

This is my action group:

export const ItemsActions = createActionGroup({
  source: 'Items/API',
  events: {
    updateItem: props<{ url: string; item: { id: number; name: string; } }>(),
    updateItemSuccess: props<{ item: Update<{ id: number; name: string; }> }>(),
    failure: props<{ error: string }>(),
  },
});

This is my reducer stuff:

export interface ItemsState extends EntityState<{ id: number; name: string; }> {
  selectedId?: string | number | null;
  loaded: boolean;
  error?: string | null;
}

export interface ItemsPartialState {
  readonly [ITEMS_FEATURE_KEY]: ItemsState;
}

export const itemsAdapter: EntityAdapter<{ id: number; name: string; }> = createEntityAdapter<{ id: number; name: string; }>();

export const initialItemsState: ItemsState = itemsAdapter.getInitialState({
  loaded: false,
});

const reducer = createReducer(
  initialItemsState,

  on(ItemsActions.updateItem, (state) => ({
    ...state,
    loaded: false,
    error: null,
  })),
  on(ItemsActions.updateItemSuccess, (state, { item }) => {

    // Yea! Currently item type is { id: number; name: string; }
    // And that doesn't work with updateOne method.

    return itemsAdapter.updateOne(item, {
      ...state,
      loaded: true,
      error: null,
    });
  }),
);

Any help would be appreciated. Thanks.


Solution

  • updateOne and updateMany want you to specify id and changes.

    Here the docs: Entity Updates.

    So your code will be:

    on(ItemsActions.updateItemSuccess, (state, { item }) => {
        return itemsAdapter.updateOne(
            { id: item.id, changes: item },
            { ...state, loaded: true, error: null }
        );
    }),