What is the best way to refresh NGRX data?

There are two models in a one-to-many relationship:

export interface GroupModel {
  id: number;
  name: string;
  userIds?: number[];
}
export interface UserModel {
  id: number;
  name: string;
  groupId?: number;
}

An issue arises when updating either model with an effect, the changes do not update the corresponding association:

groups.effects.ts

updateGroup$ = createEffect(() =>
  this.actions$.pipe(
    ofType(GroupsActions.updateGroup),
    concatMap(({ group }) =>
      this.groupsService.update(group).pipe(
        map(() =>
          GroupsAPIActions.groupUpdatedSuccess({
            update: { id: group.id, changes: group },
          })
        ),
        catchError((error) =>
          of(GroupsAPIActions.groupUpdatedFail({ message: error }))
        )
      )
    )
  )
);

users.effects.ts

updateUser$ = createEffect(() =>
  this.actions$.pipe(
    ofType(UsersActions.updateUser),
    concatMap(({ user }) =>
      this.usersService.update(user).pipe(
        map(() =>
          UsersAPIActions.userUpdatedSuccess({
            update: { id: user.id, changes: user },
          })
        ),
        catchError((error) =>
          of(UsersAPIActions.userUpdatedFail({ message: error }))
        )
      )
    )
  )
);

The reducer update methods are as follows:

on(
  GroupsActions.groupStation,
  (state) =>
    ({
      ...state,
      loading: true,
      errorMessage: '',
    })
),
on(GroupsAPIActions.groupUpdatedSuccess, (state, { update }) =>
  groupsAdapter.updateOne(update, {
    ...state,
    loading: false,
  })
),
on(
  GroupsAPIActions.groupUpdatedFail,
  (state, { message }) =>
    ({
      ...state,
      loading: false,
      errorMessage: message,
    })
)

To summarize, let's consider the following models:

groups: [
  { id: 1, name: "Group 1", userIds?: [1, 2] }
];
users: [
  { id: 1, name: "User 1", groupId: 1 }
  { id: 2, name: "User 2", groupId: 1 }
];

If we call the dispatch method to remove User 2 from Group 1:

this.store.dispatch(UserActions.updateUser({
    user: { id: 2, name: "User 2", groupId: undefined }
});

We want Group 1 to remove the userID from the userIds array as well:

{ id: 1, name: "Group 1", userIds?: [1] }

What would be a suitable approach to achieve this? Is there a standard solution or must custom coding be used?

Edit:

I have manually written the following code for the groupsReducer, but it seems like too much code for a simple task.

on(UsersAPIActions.userUpdatedSuccess, (state, { update }) => {
  const userId = +update.id;
  const newGroupId = update.changes.groupId;
  const allGroups = selectAll(state);

  // Find the group that contains the userId
  const groupToUpdate = allGroups.find((group: GroupModel) =>
    group.userIds.includes(userId)
  );

  const updateGroupChanges = {} as Partial<GroupModel>;

  if (groupToUpdate) {
    
    if (newGroupId) {
      
      if (groupToUpdate.id === newGroupId) return state;
      
     else {
        const oldGroupChanges = {
          userIds: groupToUpdate.userIds.filter((id) => id !== newGroupId),
        } as Partial<GroupModel>;
        const newGroupChanges = {
          userIds: [...groupToUpdate.userIds, newGroupId],
        } as Partial<GroupModel>;

        
        console.log(`Group ${groupToUpdate.id}: Moving user ${userId} to group ${newGroupId}`);
        
       
        return groupsAdapter.updateMany(
          [
            { id: groupToUpdate.id, changes: oldGroupChanges },
            { id: newGroupId, changes: newGroupChanges },
          ],
          { ...state }
        );
      }

     
      
    } 
    else {
      

      updateGroupChanges.userIds = groupToUpdate.userIds.filter(
        (id) => id !== newGroupId
      );
    }
  } 
  
  else {
   
    if (!newGroupId) return state;

    const newGroup = selectGroupById(newGroupId)(state);
    if (!newGroup) {
      console.warn(`Group ${newGroupId}: Not found! Could not add user ${userId}`);
      return state;
    }

  
    console.log(`Group ${newGroupId}: Adding user ${userId}`);

    updateGroupChanges.userIds = [...newGroup.userIds, userId];
  }

   
  return groupsAdapter.updateOne(
    {
      id: newGroupId,
      changes: updateGroupChanges,
    } as Update<GroupModel>,
    { ...state }
  );
}),

Answer №1

In order to refresh the status of both entities, the reducers will monitor all relevant actions to update their respective states.

For instance, the group reducer will track not only groupUpdatedSuccess as it currently does, but also the action userUpdatedSuccess.

Similarly, the user reducer will be attentive to both userUpdatedSuccess and groupUpdatedSuccess actions in order to handle updates accordingly.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm new to NPM and Node.js and I'm currently exploring the differences between global and local dependencies installations, and how they are displayed in the package.json file

I'm primarily a designer who is diving into coding. Recently, I've been assisting with an Angular project at work and needed to learn how to utilize npm to install Angular CLI and its dependencies. Setting up my Angular project was a breeze. I m ...

Is there a way to view the console in a released apk?

Currently working with Ionic and in need of exporting a release APK to be able to monitor the console for any potential issues. I am aware that using 'ionic cordova run --device' allows me to view the console, but it only shows a debug APK. Is t ...

Creating dynamic HTML templates in Webstorm using template strings with Angular 2

As mentioned in the release notes for WebStorm 2016.1, there is an image displayed. https://i.sstatic.net/9h1Yg.png Check this out here However, when I try to replicate it, mine ends up looking like this https://i.sstatic.net/ScP8W.png Do I need to ma ...

Dynamic Mat-select-trigger that automatically adjusts its size to fit the content

Currently, I am experimenting with an Angular Mat-Select that allows multiple selections. To display the selected values in the value field, I have implemented a custom Mat-Select-Trigger. My goal is to enable automatic resizing of the value field (similar ...

Height and placeholder issue with ion-searchbar

Hey there, I hope everything is going well for you. I've encountered a problem with the Ionic 5 - Angular Searchbar feature. This is how I added the searchbar: <ion-searchbar [(ngModel)]="text" (ionChange)="sear ...

How can I use Angular to dynamically open a video that corresponds to a clicked image?

I need assistance with a functionality where clicking on an image opens a corresponding video on the next page. The issue is that all images have the same ID, making it difficult to link each image to its respective video. Below is the data I am working ...

"Subscribing in interceptor doesn't seem to have any effect

I am currently facing an issue where I try to refresh a token using an API call, but the token does not get refreshed and the logs are not appearing as expected. In my code, I have implemented a timeout for testing purposes to trigger the token refresh and ...

Angular 6 issue: Data not found in MatTableDataSource

Working on implementing the MatTableDataSource to utilize the full functionality of the Material Data-Table, but encountering issues. Data is fetched from an API, stored in an object, and then used to create a new MatTableDataSource. However, no data is b ...

The module named "mongoose" does not have any member called 'PaginateResult' exported

I'm facing an issue while trying to add the necessary types for "mongoose-paginate" in my Angular 4 project setup with "angular-cli". The problem arises when Webpack throws an error. import {PaginateResult} from "mongoose"; ... getAll(page: number) ...

Fixing 404 Errors in Angular 2 Due to Component Relative Paths in SystemJS-Builder

I recently posted this on https://github.com/systemjs/builder/issues/611 My goal is to bundle my Angular 2 rc 1 application using systemjs-builder 0.15.16's buildStatic method. In my Angular component, there is a view and one or more external stylesh ...

A method for modifying the key within a nested array object and then outputting the updated array object

Suppose I have an array called arr1 and an object named arr2 containing a nested array called config. If the key in the object from arr1 matches with an id within the nested config and further within the questions array, then replace that key (in the arr1 ...

Instructions on retrieving keyboard input values from Angular's Material Datepicker using Reactive Forms

Currently, I am using Angular along with material datepicker within Reactive Forms and moment's MomentDateModule. My concern lies in extracting the value that a user types into the form via keyboard input. If you wish to see an example of this scenar ...

Issue: Vue TypeScript module not foundDescription: When using

Is there anyone out there who can assist me with this issue regarding my tsconfig.json file? { "compilerOptions": { "target": "esnext", "module": "esnext", "moduleResolution": " ...

After upgrading to Angular 15, the Router getCurrentNavigation function consistently returns null

Since upgrading to angular 15, I've encountered a problem where the this.router.getCurrentNavigation() method is returning null when trying to access a state property passed to the router. This state property was initially set using router.navigate in ...

Combining attributes of objects in an array

Is the title accurate for my task? I have an array structured like this: { "someValue": 1, "moreValue": 1, "parentArray": [ { "id": "2222", "array": [ { "type": "test", "id": "ID-100" }, { ...

Add the arrivalDate value to the existing array

Is there a way to store each arrivalDate from the API's JSON response into my array list, even though the array is currently empty? Here is a snippet of the JSON returned by the API: { "reservations": { "reservationInfo&quo ...

What makes TypeScript believe that the variable could possibly be undefined when it is clearly not the case?

I recently encountered an issue where TypeScript incorrectly identifies a variable as possibly being undefined. Here is a simplified example: const func = (val1?: boolean, val2?: boolean) => { if (!val1 && !val2) return; let result: boolean; ...

Tips for fixing the TS2345 compilation error when working with React

Attempting to implement the setState method in React has resulted in a compile error. Any solutions to this issue would be greatly appreciated. Frontend: react/typescript articleApi.tsx import axios from 'axios'; import {Article} from '../ ...

Adjust each module import to accommodate a singleton dependency

I encountered a scenario in my application that involves the use of an ApiModule: Within this module, there are two services - ApiRouteService and ApiRouteLoaderService, both scoped to the module itself. The purpose of these services is as follows: ApiRo ...

Encountering a node-sass problem during npm installation in my Angular project on Mac OS

While attempting to install node_modules for my Angular project on Mac OS, I encountered an issue with node-sass. My Node.js version is v16.13.2 and the node-sass version in the project is ^4.14.1. The package.json files can be viewed in image1 and image2. ...