In an Angular 11 app, I have a simle service that mekes a get
request and reads a JSON.
The service:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Promo } from '../models/promo';
@Injectable({
providedIn: 'root'
})
export class PromoService {
public apiURL: string;
constructor(private http: HttpClient) {
this.apiURL = `https://api.url.com/`;
}
public getPromoData(){
return this.http.get<Promo>(`${this.apiURL}/promo`);
}
}
In the the component, I need to compare the array of products with the array of campaign products (included in the JSON mantioned above) and higlight the promoted products:
export class ProductCardComponent extends DestroyableComponent implements OnInit, OnChanges
{
public promoData: any;
public promoProducts: any;
public isPromoProduct: boolean = false;
public ngOnInit() {
this.getCampaignData();
}
public ngOnChanges(changes: SimpleChanges): void {
this.getCampaignData();
}
public getPromoData() {
this.promoService.getPromoData().pipe(takeUntil(this.destroyed$)).subscribe(data => {
this.promoData = data;
this.promoProducts = this.promoData.products;
let promoProduct = this.promoProducts.find((product:any) => {
return this.product.unique_identifier == product.unique_identifier;
});
if (promoProduct) {
// Update boolean
this.isPromoProduct = true;
}
});
}
}
In the component's html file (template), I have:
<span *ngIf="isPromoProduct" >Promo</span>
There are no compilation errors.
The problem
For a reason I have been unable to understand, the template does not react to the change of the variable isPromoProduct
and the template is not updated, despite the fact that I call the function inside ngOnInit
and ngOnChanges
.
Questions:
- Where is my mistake?
- What is a reliable way to update the template?
CodePudding user response:
subscribing
to Observable
inside .ts
file it's mostly not a best practice.
try to avoid it by using async pipe
of Angular
.
you need to store the observable in the variable and not the data returned from the observable, for example:
// this variable holds the `observable` itself.
this.promoData$ = this.promoService.getPromoData()
and then in the template you can do it like this:
<div *ngIf="promoData$ | async as promoData">
here you can access the promoData
</div>
you can still use pipe()
to map the data etc but avoid the subscribe()
CodePudding user response:
The isPromoProduct
boolean is not an input. The ngOnChanges
gets triggered for changes on your properties that are decorated with the @Input
decorator. For your particular case, you can inject the ChangeDetectorRef
and trigger change detection manually:
constructor(private cdr: ChangeDetectorRef) {}
// ...
public getPromoData() {
this.promoService.getPromoData().subscribe(data => {
// ...
if (promoProduct) {
// Update boolean
this.isPromoProduct = true;
this.cdr.detectChanges();
}
});
}
You also don't need to manage httpClient
subscriptions. The observables generated by a simple get
or post
request will complete after they emit the response of the request. You only need to explicitly manage the unsubscribe for hot observables (that you create from subjects that you instantiate yourself).