Home > Mobile >  How can i show a spinner while waiting for data from a get call inside my ngOnInit ? (Angular 15)
How can i show a spinner while waiting for data from a get call inside my ngOnInit ? (Angular 15)

Time:12-20

i'm struggling on something that should be pretty easy. I'm trying to render a spinner, whenever a get call is ongoing, so instead of displaying an empty screen, i can use the spinner.

I thought of using two separate div, controlled by two ngIf, related to the same bool flag. Of course if one is *ngIf="flag", the other one is *ngIf="!flag".

I edit the value, inside the 'subscribe' of the my get call, but unfortunately, the bool (although it changes), does not affect the html (probably because how angular works, and lifecycle of the components).

Do you know how can i do this ?

In my data service component i have a really simple http get to fill my variable 'products : Product[]', and it works.

In my component shop.ts i have

@Component({
  selector: 'app-shop',
  templateUrl: './shop.component.html',
  styleUrls: ['./shop.component.css'],
})
export class ShopComponent {
  constructor(public ds: DataService) {}

  /* Variables */
  products: Product[] = [];
  isDataLoaded: boolean = false;

  /* With this get call, we get all the products informations, and we save'em 
  into products */
  ngOnInit() {
    this.ds.getProducts().subscribe((resp) => {
      this.products = resp as Product[];
      this.isDataLoaded = true;
      }
    });
  }

In the component html i just have

<div *ngIf="!isDataLoaded">
  <mat-spinner></mat-spinner>
</div>

<div *ngIf="isDataLoaded">
  Data is loaded
</div>

CodePudding user response:

this.ds.getProducts()
 .pipe(finalize(() => this.isDataLoaded = true))
 .subscribe((resp) => {
      this.products = resp as Product[];
  }
});

CodePudding user response:

Can you show how you implemented the getProduts method?

I tried to replicate your project, like this:

constructor(public ds: DataService) {}

/* Variables */
products: Product[] = [];
isDataLoaded: boolean = false;

/* With this get call, we get all the products informations, and we save'em 
into products */
ngOnInit() {
this.ds.getProducts()
  .subscribe((resp) => {
    this.products = resp;
    this.isDataLoaded = true;
  });

}

And I implemented the Data Service like this:

  constructor(private http: HttpClient) {}

  getProducts(): Observable<Product[]> {
     return this.http.get<Product[]>('API');
  }

And it works. Maybe it works for you too, but the data are loaded so fast that you don't see the spinner.

CodePudding user response:

I do this all the time. Here is the approach I use.

Store the result in a subscription and set it equal to the request like so:

@Component({
  selector: 'app-shop',
  templateUrl: './shop.component.html',
  styleUrls: ['./shop.component.css'],
})
export class ShopComponent {
  constructor(public ds: DataService) {}

  products: Product[] = [];
  isDataLoaded$: Subscription;

  ngOnInit() {
    this.isDataLoaded$ = this.ds.getProducts().subscribe((resp) => 
      this.products = resp as Product[];
    );
  }
}

Then in your template, check if the subscription exists and is not closed:

<mat-spinner *ngIf="isDataLoaded$ && !isDataLoaded$.closed"></mat-spinner>

<div *ngIf="isDataLoaded$ && isDataLoaded$.closed">
  Data is loaded
</div>

Problems with your original approach

If that request fails, your isDataLoaded variable will never update since you don't have an error block. Also, once you set that variable to true, it stays true. What happens if the user fires that request again? You need to also reset it back to false before each request so the spinner shows up.

Here is an improved version of your original code, although I do not recommend going with this approach.

ngOnInit() {
    this.isDataLoaded = false;

    this.ds.getProducts().subscribe((resp) => {
        this.products = resp;
        this.isDataLoaded = true;
      }, error => {
          ...
          this.isDataLoaded = true;
      });
 }
  • Related