Home > Software design >  Notifications: Angular updating the array variable but not refreshing the HTML view using websocket
Notifications: Angular updating the array variable but not refreshing the HTML view using websocket

Time:07-17

I have done implemention of RXJS websocket RXJS Websockets for my angular application. It is used to show the notifications on the homepage which is route /home. So once the user logins in, he receives the notifications in a mat card and a count in a mat badge using angular material lib, until this everything is fine. Now if I route to another page say for example /games, I still receive my WS messages which is fine. But now if I revisit home page /home I don't see the new WS message which I had received being at /games route, it shows the old count. This is problem one. The second problem is now if I try to send any new live message to my angular application it listens to it properly, also updates the variable which is notificationList in this case but doesn't sync with HTML view.It shows the count which we had when we logged in. Below is the snippet of my code:

Here is the HTML part which contains count and notification list:

    <button mat-button #menuTrigger="matMenuTrigger" [matMenuTriggerFor]="notifications"
    <mat-icon [matBadge]="counter" [matBadgeHidden]="counter < 1" matBadgeColor="warn">notifications</mat-icon>
</button>
<mat-menu #notifications="matMenu">
<ul>
    <li *ngFor="let item of notificationList; let i = index">
        
    </li>
</ul>
</mat-menu>

Here is the TS part which contains RXJS websocket code a short snippet of my code:

import { webSocket } from 'rxjs/webSocket';
public retryConfig = 3000;
public myWebSocket: WebSocketSubject<any>;
this.url = 'ws://localhost:8081';
fetchNotificationData() {
    this.myWebSocket = webSocket(this.url);
    this.myWebSocket.pipe(
      retry(this.retryConfig)).subscribe(
      (dataResponse: any) => {
        this.counter = this.dataResponse.length;
        this.notificationList = this.dataResponse;
      },
      // Called whenever there is a message from the server
      (err: any) => console.error("CONNECTION FAILED::", JSON.stringify(err)),

      // Called if WebSocket API signals some kind of error
      () => console.log('complete')
      // Called when connection is closed (for whatever reason)
    );
  }

Any help or advice will be appreciated. Thanks a ton.

CodePudding user response:

I'll jump in before anyone suggests using ngrx or other store pattern type things, though that's pretty much what you're wanting.

I assume that each component is using that service and fetching notification data themselves, independently?

That's what the store pattern fixes. The store deals with getting the data, the components just ask for what it's got and display it.

I highly recommend not bloating your project with ngrx to do this, however.

Angular has singleton services that will do this with a minimum of implementation.

The basics:

  • Singleton service that will do what it's doing now - web socket that data - and SAVE it
  • Provide access to that saved data for consuming components

Your notification service should simply keep track of the notifications, and either through observables or even simple accessors, allow the components to get the list. Ideally observables, I guess, so that they can subscribe to the updates live.

You're already doing the saving part, by the looks of it:

(dataResponse: any) => {
    this.counter = this.dataResponse.length;
    this.notificationList = this.dataResponse;
},

Set up something that the components and listen to and push it out through that. If I recall correctly, BehaviorSubjects store the last thing they pushed out so that initial subscriptions get some immediate data (unlike basic observables).

Forgive the (likely) slightly wrong implementation, I've barely used them compared to observables:

public notifications$: BehaviorSubject<NotificationDto[]> = new BehaviorSubject({...});

And push your data over it when it comes in:

(dataResponse: any) => {
    this.counter = this.dataResponse.length;
    this.notificationList = this.dataResponse;
    this.notifications$.next(this.notificationList);
},

And your components just listen to that bad boi:

private sub: any;
public ngOnInit(): void {
  this.sub = this.service.notifications$
    .subscribe((x: NotificationDto[]) => this.onNotifications(x));
}

public ngOnDestroy(): void {
  if (this.sub) this.sub.unsubscribe();
}

private onNotifications(notifications: NotificationDto[]): void {
  this.notificationList = notifications;
  this.count = notifications.length;
}

CodePudding user response:

It may happen if your code is running outside of angular zone. Son you can try to update your variable inside the angular zone and the view will detect changes.

constructor(private _ngZone: NgZone) {}

fetchNotificationData() {
    this.myWebSocket = webSocket(this.url);
    this.myWebSocket.pipe(
      retry(this.retryConfig)).subscribe(
      (dataResponse: any) => {
        // ———-> Here you get inside angular zone
        this._ngZone.run(() => {
          this.counter = this.dataResponse.length;
          this.notificationList = this.dataResponse;
      });
      // Called whenever there is a message from the server
      (err: any) => console.error("CONNECTION FAILED::", JSON.stringify(err)),

      // Called if WebSocket API signals some kind of error
      () => console.log('complete')
      // Called when connection is closed (for whatever reason)
    );
  }

If it doesn’t work so you can simply detect changes on view.

constructor(private ref: ChangeDetectorRef)

fetchNotificationData() {
    this.myWebSocket = webSocket(this.url);
    this.myWebSocket.pipe(
      retry(this.retryConfig)).subscribe(
      (dataResponse: any) => {
        this.counter = this.dataResponse.length;
        this.notificationList = this.dataResponse;
        // ———-> Force view to detect changes.
        ref.detectChanges();
      },
      // Called whenever there is a message from the server
      (err: any) => console.error("CONNECTION FAILED::", JSON.stringify(err)),

      // Called if WebSocket API signals some kind of error
      () => console.log('complete')
      // Called when connection is closed (for whatever reason)
    );
  }

  • Related