Real-time component update at Angular

Asked

Viewed 374 times

0

Hey, guys, all right?

I’m starting my studies with angular and I’m having an implementation problem. I have an API, developed by me, in which the returned data is updated every 20 seconds on the server.

I’m mounting an angled frontend. The idea is to update the user view every 20 seconds too (or less until). But there is a functional requirement of the application, which boils down to notifying (visually) the user, when attribute value is changed.

For a component rendered as follows:


<div *ngFor="event in events">
    <app-event
        [id]="event.id"
        [time]="event.time"
        [value1]="event.value1" >
    </app-events>
</div>

Being an example call (call n)

"events" : [
    {
        "id" : 1
        "time" : 45,
        "value1" : 1.6
    },
    {
        "id" : 2
        "time" : 78,
        "value1" : 1.5
    }
]

In the n+1 call:

"events" : [
    {
        "id" : 1
        "time" : 46,
        "value1" : 1.5
    },
    {
        "id" : 2
        "time" : 78,
        "value1" : 1.6
    }
]

The values of time and value1 were changed in the api in the n+1 call, therefore, I need to change the Component class that it was rendered in call n. I read about issues involving Subject, Observable and other things, but it wasn’t so clear to me, how to perform this.

Below the current code of my components.

events.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { EventsService } from '../../services/events.service';


@Component({
  selector: 'app-events',
  templateUrl: './events.component.html',
  styleUrls: ['./events.component.scss']
})
export class EventsComponent implements OnInit {

  events = []
  
  constructor(private eventsService: EventsService) { }

  ngOnInit(): void {

    this.eventsService.getEvents().subscribe((data: any[]) => {         
      this.events = data
    })


  }


}

events.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, Subject } from 'rxjs'

@Injectable({
  providedIn: 'root'
})
export class EventsService {

  constructor(private httpClient: HttpClient) {
    
  }

  private API_URL = "http://0.0.0.0:93"

  public getEvents(): Observable<any> {
    return this.httpClient.get(`${this.API_URL}/events/inplay`)    
  }
}

events.component.html

<div *ngFor="let event of events.data">
    <app-event [event]="event"></app-event>
</div>

event.component.ts

import { Component, Input, OnChanges,  } from '@angular/core';
import { SimpleChanges } from '@angular/core'
import { EventsComponent } from '../events.component';


@Component({
  selector: 'app-event',
  templateUrl: './event.component.html',
  styleUrls: ['./event.component.scss']
})
export class EventComponent implements OnChanges {

  @Input() event: any;

  constructor(private eventsComponent: EventsComponent) {

   }


  ngOnInit(): void {       

  }

  ngOnChanges(changes: SimpleChanges) {       
    console.log(changes)    
  }
 
}

Thanks.

  • So much Subject as Behaviour Subject are indicated for data updates between components that need to update values of one component through another component rather than data from an API. I think this is very simple to solve by inserting a method JavaScript setInterval() on the service call in the file events.component.ts, this would update the data at the time determined in the method, or, use methods specific to the Angular/RXJS for this, as the timer: https://rxjs.dev/api/index/function/timer

  • All right, I even did that and I forgot to put it in the sample codes. The problem is: How will I identify that the data has been changed and notify the user (visually, a class change, for example) of this change?

  • I don’t know if there are methods ready for this, or, I would have to create in hand. But if you’re not ready you can compare the values of the objects in this.Events. Takes the current value and checks if the value the update brought is still equal to the current one.

  • 1

    Yes, currently this application is built in Jquery and I do exactly like this. I believe it will be the only alternative.

  • 1

    You can use sockets instead of http, then your back notifies the front that something has changed

1 answer

0

If your backend still doesn’t offer Websockets or SSE, you can do it through Polling:

public ngOnInit(): void {
    interval(20000).pipe(
        mergeMap(() => this.eventsService.getEvents()),
        distinctUntilChanged((prev, nxt) => prev.time === nxt.time && prev.value1 === nxt.value1),
    ).subscribe(
        evt => console.log(`time or value1 changed`, evt),
    )
}

If your backend offer SSE, you could let the comparison logic in it and in frontend only receive the events:

public ngOnInit(): void {
    const eventSource = new EventSource('url/do/sse');
    eventSource.onmessage = (event: MessageEvent) => {
        console.log(`received server event`, event);
    };
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.