Search code examples
javascripthtmlangulartypescript

How to Display Dynamic Updates with rxjs in HTML


This question is based on an example from https://github.com/ParthibanRajalingam/demos

Only the initial values of the stock object are displayed using app.component.html: enter image description here

    <div class="card" *ngIf="stock$ | async; let stock">
  <div class="textBox">
    <div class="textContent">
        <div class="h1">{{stock.name}}<div *ngIf="stock.increased"
          style="color: green ; margin: 0 10px 0 10px; float: right;">▲</div>
        <div *ngIf="!stock.increased" style="color: red ; margin: 0 10px 0 10px; float: right;">▼</div>
      </div>
    </div>
    <div class="detail">Price: ${{stock.price}}   
    </div>
  </div>
</div>

The app.component.ts uses BehaviorSubject and Observable from rxjs:

private webSocket: WebSocket;
private subscription: Subscription = Subscription.EMPTY; 
public stock: Stock = new Stock();
public stockSubject: BehaviorSubject<Stock> = new BehaviorSubject<Stock>(this.stock);
public stock$: Observable<Stock> = this.stockSubject.asObservable();

this.webSocket = new WebSocket('ws://localhost:8080/stocks');
this.subscription = this.stock$.subscribe();
        
this.webSocket.onmessage = (event) => {
    this.stock = JSON.parse(event.data.toString());
    this.stockSubject.next(this.stock);
    console.log(JSON.stringify(this.stock));
};  

The Stock object has public attributes to avoid using the any type:

export class Stock {
  name: string;
  icon: string;
  price: number;
  increased: boolean;
  
  constructor () {
    this.name = 'Amazon';
    this.icon = '';
    this.price = 0;
    this.increased = false;
  }
}

The Angular application is a web service client that receives updated stock prices and calls next on the BehaviorSubject:

{"name":"Amazon","icon":"https://cdn.cdnlogo.com/logos/a/77/amazon-dark.svg","price":12.17,"increased":false}
{"name":"Amazon","icon":"https://cdn.cdnlogo.com/logos/a/77/amazon-dark.svg","price":12.72,"increased":true}
{"name":"Amazon","icon":"https://cdn.cdnlogo.com/logos/a/77/amazon-dark.svg","price":12.71,"increased":false}

What is missing to display the updated stock properties on the HTML page? The original example does not use Subjects or Observables and worked for the author.


Solution

  • Please find below working example for your reference!

    article

    import { CommonModule } from '@angular/common';
    import { Component } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import { BehaviorSubject, map, Observable, Subscription, interval } from 'rxjs';
    import 'zone.js';
    import { Stock } from './stock.model';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [CommonModule],
      template: `
        <div class="card" *ngIf="stock$ | async as stockData">
          <div class="textBox">
            <div class="textContent">
                <div class="h1">{{stockData.name}}<div *ngIf="stockData.increased"
                  style="color: green ; margin: 0 10px 0 10px; float: right;">▲</div>
                <div *ngIf="!stockData.increased" style="color: red ; margin: 0 10px 0 10px; float: right;">▼</div>
              </div>
            </div>
            <div class="detail">Price: $ {{stockData.price}} </div>
          </div>
        </div>
      `,
    })
    export class App {
      // private webSocket: WebSocket;
      public stock: Stock = new Stock();
      public stockSubject: BehaviorSubject<Stock> = new BehaviorSubject<Stock>(
        this.stock
      );
      public stock$: Observable<Stock> = this.stockSubject.asObservable();
    
      // this.webSocket = new WebSocket('ws://localhost:8080/stocks');
      // this.subscription = this.stock$.subscribe();
    
      ngOnInit() {
        interval(1000)
          .pipe(
            map(() => ({
              data: {
                name: 'Amazon',
                increased: Math.random() < 0.5,
                price: Math.floor(Math.random() * 100) + 1,
              },
            }))
          )
          .subscribe(this.onmessage);
      }
    
      onmessage = (event: any) => {
        this.stock = event.data;
        this.stockSubject.next(this.stock);
      };
    }
    
    bootstrapApplication(App);
    

    stackblitz