user10298495
user10298495

Reputation:

Display Spinner till API Response in angular 6

I am new to Angular 6 and no experience in any version of angular previously & creating my website using angular 6, in which i have to display spinner till API Response is pending.I am using httpclient of angular 6 to call APIs. I want to show spinner for every API request. I searched on SO but didn't find answer of my question.Below is my component & services file.

/* data.services.ts */

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})

export class DataService {
  constructor(private http: HttpClient) { }
  getUsers() {
    return this.http.get('https://jsonplaceholder.typicode.com/users')
  } 
}

/* users.component.ts *

import { Component, OnInit } from '@angular/core';
import {DataService} from '../data.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.css']
 })

export class UsersComponent implements OnInit {
  users$: Object;
  constructor(private data: DataService) { }
  ngOnInit() {
    this.data.getUsers().subscribe(data => {
      this.users$ = data; 
    });
  }
}

Note: I don't want to use any package or module for that.

Can anyone please help me to solve this problem? Any help would be appreciated.

Upvotes: 10

Views: 47234

Answers (5)

Florian Motteau
Florian Motteau

Reputation: 3714

You could store a reference on the subscription as a users component property, then use a simple *ngIf in your template to show/hide the loading indicator :

/* users.component.ts *

import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';
import { Observable, Subscription } from 'rxjs';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.css']
})
export class UsersComponent implements OnInit {
  users$: Object;
  subscription: Subscription

  constructor(private data: DataService) { }

  ngOnInit() {
    this.subscription = this.data.getUsers().subscribe(data => {
      this.users$ = data; 
    });
  }
}

In your template:

...
<div *ngIf="!subscription?.closed">loading...</div>
...

See http://reactivex.io/rxjs/class/es6/Subscription.js~Subscription.html for more infos on Subscription class.

It's beyond this question scope but you also should check that your subscription is closed when your component is destroyed (so keeping a reference on the subscription can help): call subscription.unsubscribe() if necessary in ngOnDestroy() for example, or extend from a base class for this purpose (so all of your components could benefit from it).

See https://brianflove.com/2016/12/11/anguar-2-unsubscribe-observables/ or https://netbasal.com/automagically-unsubscribe-in-angular-4487e9853a88

Upvotes: 1

Buczkowski
Buczkowski

Reputation: 2416

For that purpose I would use http interceptor for checking certain events during http request. Let's have component which will have some loader and put that component on same level like router outlet or app component. From our interceptor we will communicate with service to tell our component whenever show loader or not.

There is simple angular 6 example for that: https://stackblitz.com/edit/angular-hgxcsu

Upvotes: 2

Adrian Brand
Adrian Brand

Reputation: 21638

If you insist on not installing any packages then use a boolean flag to tell if the loading has finished yet

Get a loading animation from https://loading.io/ to put in the loading section of the html

/* data.services.ts */

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})

export class DataService {
  constructor(private http: HttpClient) { }
  getUsers() {
    return this.http.get('https://jsonplaceholder.typicode.com/users')
  } 
}

/* users.component.ts *

import { Component, OnInit } from '@angular/core';
import {DataService} from '../data.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.css']
 })

export class UsersComponent implements OnInit {
  users: Object; // <-- Do not use $ at the end of a variable unless it is an observable
  loading = true;
  constructor(private data: DataService) { }
  ngOnInit() {

    this.data.getUsers().subscribe(data => {
      this.users = data;
      this.loading = false;
    });
  }
}

<div *ngIf="loading else loaded">
    loading ...
</div>

<ng-template #loaded>
    <div *ngFor="let user of users">{{user.name}}</div>
</ng-template>

Upvotes: 13

Adrian Brand
Adrian Brand

Reputation: 21638

Take a look at my package ngx-RxCache. Dealing with loading is one of the key use cases. Install ngx-rxcache. Don't subscribe to observables in your TypeScript, use the async pipe in your view. Check out a demo at https://stackblitz.com/edit/angular-3yqpfe

Get a loading animation from https://loading.io/ to put in the loading section of the html

/* data.services.ts */

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { RxCacheService } from 'ngx-rxcache';
import { User } from 'path to user interface definition';

@Injectable({
  providedIn: 'root'
})

export class DataService {
  constructor(private http: HttpClient, private cache: RxCacheService) { }
  load() {
    this.usersCacheItem.load();
  }

  private usersCacheItem = this.cache.get<User[]>({
      id: '[User] users',
      construct: () => this.http.get<User[]>('https://jsonplaceholder.typicode.com/users')
    });

    users$ = this.usersCacheItem.value$;
    loading$ = this.usersCacheItem.loading$;
}

/* users.component.ts *

import { Component, OnInit } from '@angular/core';
import {DataService} from '../data.service';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  styleUrls: ['./users.component.css']
 })

export class UsersComponent implements OnInit {
  users$: this.data.users$;
  loading$: this.data.loading$;
  constructor(private data: DataService) {
    data.load();
  }
}
<div *ngIf="loading$ | async; else loaded">
    loading ...
</div>

<ng-template #loaded>
    <div *ngFor="let user of users$ | async">{{item}}</div>
</ng-template>

Upvotes: 0

Vikas
Vikas

Reputation: 12036

You can use ng4-loading-spinner

Execute npm i ng4-loading-spinner --save

Import module to your application root module

import { Ng4LoadingSpinnerModule } from 'ng4-loading-spinner';

Make an import entry

 imports: [ Ng4LoadingSpinnerModule.forRoot() ]

Include spinner component to your root level component.

<ng4-loading-spinner> </ng4-loading-spinner>

use show() and hide() inside subscribe callback

   import { Ng4LoadingSpinnerService } from 'ng4-loading-spinner';
     constructor(
            private spinnerService: Ng4LoadingSpinnerService,
            private data: DataService
        ) { }
       ngOnInit() {
         this.spinnerService.show();//show the spinner
    this.data.getUsers().subscribe(data => {
      this.users$ = data; 
       this.spinnerService.hide();//hide the spinner if success
    },
    (error)=>this.spinnerService.hide();//hide the spinner in case of error
 );
  }}

Live Demo

Upvotes: 2

Related Questions