user2620132
user2620132

Reputation: 175

how to update or remove the nested object inside ngrx entities?

how to update or remove nested object inside ngrx entities for example I want to delete the first element which has the 19 id from (charter.entities.scopes.data) as it shown in the bellow stored json object inside my ngrx store

 charter: {
      ids: [
        1
      ],
      entities: {
        '1': {
          id: 1,
          projectName: 'Some Project',
          projectCode: '899',
          projectUniqueCode: '674a9596-50ee',
          projectStatus: 'construction',
          budgetCode: 'CC34',
          projectTypeOne: 'Goods',
          projectTypeTwo: 'New',
          donorName: 'Elza Hills',
          scopes: {
            data: [
              {
                id: 19,
                module: 'Miss Cassandra Cartwright I',
                description: 'In tempore quia asperiores aut ea cum optio minima nemo est et aspernatur est repudiandae voluptas ipsum.',
                time: '2018-01-23 15:37:36'
              },

              {
                id: 43,
                module: 'Leanne Douglas',
                description: 'Occaecati facere eligendi esse esse nostrum in et vitae assumenda molestias omnis quis sit qui aut omnis est.',
                time: '2018-01-23 15:37:36'
              },
            ]
          },
          assumptions: {
            data: [
              {
                id: 29,
                assumption: 'Doloremque quo nihil minima ad optio perspiciatis asperiores debitis mollitia at debitis porro quia nam accusantium illo consequatur labore cum.',
                comments: 'Inventore ut pariatur id laboriosam recusandae soluta quo sunt impedit aut velit.'
              },
              {
                id: 164,
                assumption: 'Dolores quam aut possimus sint fugiat natus quos quaerat saepe facilis harum molestiae.',
                comments: 'Cumque quis magni illo dolore quas nam officiis dolores enim soluta doloribus in sed eum ut sunt.'
              },
            ]
          },

The Reducer

export interface State extends EntityState<ProjectsCharter> {
  isLoading: boolean;
  isLoaded: boolean;
  selectedProjectsId: any;
}

export const adapter: EntityAdapter<ProjectsCharter> = createEntityAdapter({
  selectId: (state: ProjectsCharter) => state.id,
  sortComparer: false
});

export const initialState = adapter.getInitialState({
  isLoading: false,
  isLoaded: false,
  selectedProjectsId: null
});

export function reducer(state = initialState, action: CharterActions) {
  switch (action.type) {
    case CharterActionTypes.loadCharterSuccess:
      return {
        ...adapter.addOne(action.payload['data'], state),
        isLoading: false,
        isLoaded: true
      };
    case CharterActionTypes.updateScopeOnParent:
      const scopeEntity = { ...state.entities[action.payload.param] };
      scopeEntity.scopes.data = scopeEntity.scopes.data.map(item => {
        if (item.id === action.payload.id) {
          item.module = action.payload.module;
        }
        return item;
      });

      return {
        ...adapter.updateOne(scopeEntity, state)
      };
    default:
      return {
        ...state
      };
  }
}

I can update and modify the nestad object with this reducer but the problem is 1- it's a little bit complex and also show me in terminal in compile time error TS2339: Property 'map' does not exist on type 'Scope'. also Argument of type '{ id: string; projectName: string; projectStatus: string; projectCode: string; projectUniqueCode:...' is not assignable to parameter of type 'Update<ProjectsCharter>'.

Upvotes: 10

Views: 11741

Answers (3)

Zach Gollwitzer
Zach Gollwitzer

Reputation: 2383

Let me preface this by saying the ideal solution is to normalize your entities with something like normalizr. That said, here is a solution that works for me:

  1. Make a deep clone of the state object
  2. Use the Javascript delete method on the cloned object
  3. Pass this new object as the new state

So let's assume your state looks something like this:

{
  entities: {
   '1': {
     prop1: 'val1',
     nested: {
      '1': {
        prop1: 'val1'
      },
      '2': {
        prop2: 'val2'
      }
    }
  }
}

If your goal is to delete entities[1].nested[1], just use the following code (note that I am using the lodash library):

import * as _ from 'lodash'

on(Actions.sampleDeleteAction, (state) => {
  const clonedState = _.cloneDeep(state);

  delete clonedState.entities[1].nested[1];

  return { ...clonedState };
}

Obviously, you would want to add props to your action that contain the necessary index values you are using to delete rather than explicitly using indexes as in the example entities[1].nested[1].

Upvotes: 1

Richard Matsen
Richard Matsen

Reputation: 23473

According to my logic, the following reducer mods should at least eliminate the TS2339 error.

This bit

Argument of type '{ id: string; projectName: string; projectStatus: string; projectCode: string; projectUniqueCode:...' is not assignable to parameter of type 'Update'

is just telling you (in a non-helpful way) that adapter.updateOne is requiring a first parameter of pattern { id, changes } but you are just giving it changes.


export function reducer(state = initialState, action: CharterActions) {
  switch (action.type) {

    case CharterActionTypes.loadCharterSuccess:
      ...

    case CharterActionTypes.updateScopeOnParent:

      const id = action.payload.param;
      const entities = {...state.entities};
      const original = entities[id];

      const newScopes = original.scopes.data.map(item => {
        if (item.id === action.payload.id) {
          item.module = action.payload.module;
        }
        return item;
      });
      const changes = { scopes: { data: newScopes }} // Partial<ProjectsCharter>

      return {
        ...adapter.updateOne({ id, changes }, state)
      };
    }

    default:
      ...
  }
}

Upvotes: 4

Christian Benseler
Christian Benseler

Reputation: 8065

I strongly advice you to change your store and normalize it. Take a look here: https://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html

With this approach you will have less complex reducers, as explained here https://redux.js.org/docs/recipes/reducers/UpdatingNormalizedData.html

With a store shaped this way, you will have to build a complex reducer.

Upvotes: 11

Related Questions