Search code examples
angularngrx

Multiple list with different filter for the same State - NGRX 11 ANGULAR 11


I am working on a angular 11 and NGRX 11 project. I wish to have 2 inputs select which are users but of different user type.

For example :

enter image description here

I have 2 types of user (Seller and Buyer) :

  • The first select (User Seller), I call my API backend : /api/user?type=seller&page=0&size=10
  • The secondly select (User Buyer), I call my API backend : /api/user?type=buyer&page=0&size=10

I use NGRX and I have a store User but when I call the store I return the same list of user.

How I can detach the list of user seller and list of user buyer ? (2 stores ? 2 selectors ? 2 vesions of the store ?)

user.action.ts

export const loadUsers = createAction(
  '[Users List] Load Users via Service',
  props<{ filters: UserFilter }>()
);

export const usersLoadedDone = createAction(
  '[Users Effect] Users Loaded Successfully',
  props<{ users: Page<User> }>()


export const loadUser = createAction(
  '[Users List] Load User via Service',
  props<{ userId: string }>()
);

export const createUser = createAction(
  '[Create User Component] Create User',
  props<{ event: any, user: User }>()
);

export const deleteUser = createAction(
  '[Users List Operations] Delete User',
  props<{ userId: string }>()
);


export const updateUser = createAction(
  '[Users List Operations] Update User',
  props<{ user: User }>()
);


export const userActionTypes = {
  loadUsers,
  usersLoadedDone,
  loadUser,
  createUser,
  deleteUser,
  updateUser,
};

user.reducers.ts


export interface UserState extends EntityState<User> {
  isLoading: boolean;
  totalElements: number;
  totalPages: number;
  empty: boolean;
  page: number;
  filters: UserFilter;
}

export const adapter: EntityAdapter<User> = createEntityAdapter<User>({
  selectId: user => user.userId
});

export const initialState = adapter.getInitialState({
  isLoading: false,
  totalElements: null,
  totalPages: null,
  page: 0,
  empty: true
});

export const userReducer = createReducer(
  initialState,

  on(userActionTypes.loadUsers, (state, res) => ({
    ...state,
    isLoadingList: true,
    filters: res.filters
  })),

  on(userActionTypes.usersLoadedDone, (state, action) => {
    return adapter.setAll(
      action.users['content'],
      {
        ...state,
        isLoadingList: false,
        isLoad: true,
        totalElements: action.users.totalElements,
        totalPages: action.users.totalPages,
        empty: action.users.empty,
        page: action.users.number,
      }
    );
  }),

  ...
);

export const {selectAll, selectIds} = adapter.getSelectors();

user.selectors.ts

export const userFeatureSelector = createFeatureSelector<UserState>('users');

export const getAllUsers = createSelector(
  userFeatureSelector,
  selectAll
);

...

ps : As you can see the list of users is paginated so my angular application only has part of the data


Solution

  • You can detach each user by having two different states in a feature state:

     export interface IUsersFeatureState{
      userSeller: IUserSellerState;
      userBuyer: IUserBuyerState;
     }
    

    Then you can have 2 different reducer for each:

     export const reducers: ActionReducerMap<IUsersFeatureState> = {
      userSeller: UserSellerReducer,
      userBuyer: UserBuyerReducer
     }
    

    Each reducer, you can have different actions to both load the userSeller and userBuyer:

     export interface IUserSellerState {
      loaded: boolean,
      loading: boolean,
      userSeller: UserSellerModel
     }
    
     export interface IUserBuyerState{
      loaded: boolean,
      loading: boolean,
      userBuyer: UserBuyerModel
     }
    
     export const initialState: IUserSellerState ={ 
      loaded: false,
      loading: false,
      userSeller: null
     }   
    
      on(userSellerActionTypes.loadSellerUsers, (state, res) => ({
       ...state,
       loaded: false,
       loading: true
       userSeller: res
       })),
    
       on(userBuyerActionTypes.loadBuyerUsers, (state, res) => ({
       ...state,
       loaded: false,
       loading: true
       userBuyer: res
       })),
    

    You can control the fetch from the API via Effects class as well. For the store module, you can register the reducer like below:

     StoreModule.forFeature('UsersFeature', reducers),
    

    For the selectors, you can have 2 selectors, each slicing data from the two different state.

      export const getUserFeatureState = 
       createFeatureSelector<IUsersFeatureState>('userFeature');
      
      export getUserSellerDetails = (state: IUserSellerState ) => 
      state.userSeller