im using fakeapi store to fetch products and loop on them to show in a component im using a service to do that and it goes as follows
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/environments/environment';
@Injectable({
providedIn: 'root',
})
export class ProductsService {
eleProducts: any[];
spinnerElec: boolean;
constructor(private http: HttpClient) {
this.spinnerElec = true;
this.eleProducts = [];
}
getElectronics() {
this.spinnerElec = true;
this.http
.get(environment.baseUrl '/category/electronics')
.subscribe((res: any) => {
this.eleProducts = res;
this.spinnerElec = false;
});
}
then in the electronics-component.ts
export class ElectronicsComponent implements OnInit {
Products: any[];
spinner: boolean;
constructor(public service: ProductsService) {
this.Products = [];
this.spinner = true;
}
ngOnInit(): void {
this.getProducts();
}
getProducts() {
this.spinner = this.service.spinnerElec;
this.service.getElectronics();
this.Products = this.service.eleProducts;
}
and to display data in electronics-component.html
<div *ngIf="!spinner">
<div
style="width: 18rem"
*ngFor="let product of Products"
>
<app-product
[elecCart]="service.elecCart"
[Product]="product"
(target)="addToCart($event)"
></app-product>
</div>
</div>
<div
*ngIf="spinner"
>
<app-spinner></app-spinner>
</div>
im using ngif on the spinner to clear it from the dom and show data once it fetched sucessfuly the problem is that i must open the componet then go to another component then come back to it again to show the data otherwise the data wont show... thanks in advance
CodePudding user response:
You are not returning anything in getProducts
, and you should let the component to subscribe
to that http
call, not your service.
With your current code in the service, even you add a return
to it, it will return a Subscription
, not the data you want to handle in the component.
I highly recommend the reactive
approach, which is a good practice in Angular:
service
getElectronics(): Observable<any[]> { //you should create your own type, avoid using any
return this.http.get(environment.baseUrl '/category/electronics').pipe(
tap(() => this.spinnerElect = false)
);
}
Component
theProducts$!: Observable<any[]>; //it will store the data wrapped as an Observable
ngOnInit(): void {
// store the observable from the service containing the products data
this.theProducts$ = this.service.getElectronics();
}
template HTML
<app-product
[theProducts]="theProducts$ | async"
(target)="addToCart($event)"
></app-product>
Using the async
pipe, you subscribe to the observable in the template HTML, without managing the subscription
.
CodePudding user response:
You are not suscribing in your componen, so as http is an async operation the first time you use call it in ngOnInit the code will continue and don't return anything because your component aren't listening that change.
There are two ways to avoid this, if you are not familiar with Rxjs (as i am) you can use setTimeout to create a little delay allowing your code to "wait" until the call is made.
getProducts() {
this.spinner = this.service.spinnerElec;
this.service.getElectronics();
setTimeout(() => {
this.Products = this.service.eleProducts;
}, 100);
}
BUT this is a way just for a quick fix. The correct way is to : a) return the http petition to the component and suscribe there.
b) Convert eleproducts in an observable, when the http is load use .next(res) to emit the new value. In your component you'll suscribe to that observable and it will updated
Look here to know more about how to suscribe to a service variable: https://www.learnrxjs.io/learn-rxjs/subjects/behaviorsubject