Search code examples
angulartypescriptwebsocketstate-managementngxs

Angular : NGXS : WebSocket updated the state but not UI


I'm using NGXS to implement the state management in my Angular project, and the states are updated by the WebSocket, a plugin of NGXS

What I implemented:

model.ts

export interface Student {
  id: string;
  name: string;
  passed: boolean;
}

student.action.ts

export class GetStudents{
 ...
}
export class UpdateStatus {
  static readonly type = '[Student] Update Status';
  constructor( studentId: string ) {}
}

student.state.ts

...
export class StudentStateModel {
  students: Student[];
}

@State<StudentStateModel>({
  name: 'student',
  defaults: {
    students: []
  }
})

@Action(GetStudents)
... 

@Action(UpdateStatus)
  updateStatus(
    { getState, setState }: StateContext<StudentStateModel>,
    { studentId }: UpdateStatus
  ) {
  
    const students = getState().students;
    const index = students.findIndex((i) => i.id === studentId);

    setState((state: ApprovalStateModel) => {
      state.students[index].passed = true;
      return state;
    });
  }
...

I implement 2 components (parent and child):

1/ the parent component keeps the list of students

students$: Observable<any>; // Get list of Students from Store GetStudents

students.component.html

<div *ngFor= "let student of students$ | async">
    <app-student-detail
      [student] = "student"
    ></app-student-detail>
  </div>

2/ the child component keeps the student detail, it includes an input

@Input() student: Student = null;

student-detail.component.html

<div *ngIf="student">
    <p>Id: {{student.id}}</p>
    <p>Name: {{student.name}}</p>
    <p>Result: {{student.passed}} </p>
  </div

I use the WebSocket plugin to update the student state, I sent a message

 {"type": "[Student] Update Status", "studentId" : "10001"}

Result:

From the log, the state is updated successfully with passed = true for student id 10001, but from UI, the Result is not updated.

Something wrong with my implementation, any suggestion is appreciated.


Solution

  • Try using a state operator to update the state. For example, you could use the updateItem to find and update an item in an array:

    import { patch, updateItem } from '@ngxs/store/operators';
        
    ...
    
    @Action(UpdateStatus)
    updateStatus(ctx: StateContext<StudentStateModel>, { studentId }: UpdateStatus) {
      ctx.setState(
        patch({
          students: updateItem<Student>(
            student => student.studentId === studentId,
            student => ({
              ...student,
              passed: true
            })
          )
        })
      );
    }