To wait for the first value from an observable you can use firstValueFrom()
. This method is dangerous since it can cause your application to hang indefinitely, always include a timeout mechanism.
async getProducts(): Promise<Product[]> {
const res = await firstValueFrom(
this.http.get<[]>(this.m_baseURL + '/products').pipe(timeout(10000))
);
console.log(res);
this.products = res;
return this.products;
}
A better design is to instead only make the http request to update this.products
, and just use the variable like it always has content, you can do some other things in your subscription if you want side effects to happen on update.
products: Product[] = [];
ngOnInit(){
this.updateProducts();
}
updateProducts() : void {
this.http.get<[]>(this.m_baseURL+'/products').subscribe(res => {
console.log(res)
this.products = res;
this.doSomething();
this.doSomethingElse();
});
}
Change detection will automatically update your html when an http response is received, but you can always force it with ChangeDetectorRef.detectChanges()
.
<div *ngFor="product of products">{{ product.name }}</div>
The above html will be blank until your first subscription completes, at which point it should automatically update.
If you're using a service it would look like this:
service
products: Product[] = [];
updateProducts() : void {
this.http.get<[]>(this.m_baseURL+'/products').subscribe(res => {
console.log(res)
this.products = res;
this.doSomething();
this.doSomethingElse();
});
}
component
constructor(private service: MyService){}
ngOnInit(){
this.updateProducts();
}
get products(){
return this.service.products;
}
updateProducts(){
this.service.updateProducts();
}
The two wrapper methods are just so you don't have to write service
in your html.
If you want unique side effects to happen in different components, you would convert this.products
to a subject, and then you can subscribe and execute a callback whenever products
changes. A BehaviorSubject
lets you initialize the value to an empty array.
service
products$ = new BehaviorSubject<Product[]>([]);
updateProducts() : void {
this.http.get<[]>(this.m_baseURL+'/products').subscribe(res => {
console.log(res)
this.products$.next(res);
});
}
If you want to save the value of the subject in the component rather than subscribing with the async
pipe, make sure to unsubscribe when the component is destroyed. That's because this subject does not complete like the observables from HttpClient
, so subscriptions will remain in memory unless unsubscribed.
component
sub = new Subscription();
products = [];
constructor(private service: MyService){}
ngOnInit(){
this.sub = this.service.products$.subscribe(res => {
console.log(res)
this.products = res;
this.doSomething();
this.doSomethingElse();
});
this.updateProducts();
}
updateProducts(){
this.service.updateProducts();
}
ngOnDestroy(){
this.sub.unsubscribe();
}