I have a service that always returns Observable<T>
, and I cannot change the code of this service.
Supposed I have a button, whenever the button is clicked, I call the method in the service, and it returns a new Observable. How can I update the new data to UI?
Source code and playground on StackBlitz
app.component.ts
import { Component, Injectable, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs';
@Injectable()
export class Service {
// Cannot change the code in this class
public getRandom(): Observable<number> {
return of(Math.random());
}
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
constructor(private service: Service) {}
public random: number = 0;
public random$: Observable<number> = new Observable<number>();
ngOnInit(): void {
this.random = 0;
}
buttonClick(): void {
// how can I update the random with this.service.getRandom()?
console.log('button clicked')
}
}
app.component.html
<h1>{{random}}</h1>
<button (click)="buttonClick()">Get new Random number</button>
CodePudding user response:
One easy way is to convert the Observable to a Promise and use await:
async buttonClick(): Promise<void> {
const value = await firstValueFrom(random$);
console.log('button clicked ' value);
}
If the Observable emits more than once and you want all values, use .subscribe().
CodePudding user response:
I highly recommend using the reactive approach:
HTML
<ng-container *ngIf="(random$ | async) as theRandomNumber" >
<h1>{{ theRandomNumber }}</h1>
</ng-container>
<button (click)="buttonClick()">Get new Random number</button>
ts
export class AppComponent implements OnInit {
random$: Observable<number>!; // no need to initialize it
constructor(private service: Service) {}
ngOnInit(): void {}
buttonClick(): void {
this.random$ = this.service.getRandom();
}
}
When you trigger the event click
, your public class property random$
will store the observable
from your service, then, within your template html, using the async
pipe, you subscribe to random$
, and it will react for every click
event, with this, you keep your ts file cleaner and simple
Now, if for some reason, you need to have that random number within your ts
file, you could pipe
the observable
and still keep this reactive approach:
import { tap } from 'rxjs';
export class AppComponent implements OnInit {
random$: Observable<number>!; // no need to initialize it
private random!: number;
constructor(private service: Service) {}
ngOnInit(): void {}
buttonClick(): void {
this.random$ = this.service.getRandom()
.pipe(tap((theNumber) => this.random = theNumber));
}
}