Angular 7 :: Requisições HTTP e Loader

[Atualizado em 06/Abril/2010]

Já faz algum tempo que venho trabalhando com Angular. Depois de passar anos com o AngularJS (versão 1), e meio relutante em aprender Typescript, decidi fazer o teste.

Apesar das grandes diferenças entre as versões do Angular e da mudança entre Javascript e Typescript, foi bem fácil, rápido e tranquila a virada de chave entre as frameworks. Aproveitando essa mudança, decidi aqui hoje compartilhar como faço em meus projetos para interceptar requisições HTTP e exibir uma mensagem/animação de carregamento.

O processo é bastante simples:

  1. Criar o interceptador de requisições HTTP
  2. Implementar o Listener no módulo principal
  3. Exibir a mensagem/animação

1. Criar o interceptador de requisições HTTP

O nome é feio, mas é bastante simples. A implementação consiste em dois @injectable()’s: um com o para interceptar a requisição e outro para salver o status atual.

Algumas funções do rxjs são necessárias para criar o objeto ovservável, mas o mais legado do Angular é que ele mesmo já fornece uma classe para interceptar as requisições: HttpInterceptor.

import { Injectable } from '@angular/core';
import {
    HttpEvent,
    HttpHandler,
    HttpInterceptor,
    HttpRequest
} from '@angular/common/http';

import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';

@Injectable()
export class HTTPStatus {
    private requestInFlight$: BehaviorSubject<boolean>;
    constructor() {
        this.requestInFlight$ = new BehaviorSubject(false);
    }

    setHttpStatus(inFlight: boolean) {
        this.requestInFlight$.next(inFlight);
    }

    getHttpStatus(): Observable<boolean> {
        return this.requestInFlight$.asObservable();
    }
}

@Injectable()
export class HTTPListener implements HttpInterceptor {
    private _requests = 0;
    constructor(private status: HTTPStatus,
                private router: Router) {}

    intercept(
        req: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpEvent<any>> {
        ++this._requests;
        this.status.setHttpStatus(true);
        return next.handle(req).pipe(
            map(event => {
                return event;
            }),
            catchError(error => {
                if (error.status === 401) {
                    this.auth.setToken(null);
                    this.auth.setRedirectUrl(this.router.url);
                    this.router.navigate(['/access']);
                }

                return throwError(error);
            }),
            finalize(() => {
                --this._requests;
                this.status.setHttpStatus(this._requests > 0);
            })
        );
    }
}

A classe HTTPStatus é o observável que nos trará um booleano se temos ou não uma requisição em andamento. Já a HTTPListener é a que iremos registrar como interceptadora das requisições.

2. Implementar o Listener no módulo principal

Como no geral quero que todas as requisições feita pelo projeto apresentem a mensagem/animação de carregamento, opto por colocar a interceptação no módulo principal para que se aplique em todas as requisições.

Na instalação básica do Angular, abra o app.module.ts na pasta app do seu projeto. Importe as classes criadas anteriormente:

import { HTTPListener, HTTPStatus } from './loader/interceptor';
const RxJS_Services = [HTTPListener, HTTPStatus];

E registre, dentro do providers do seu @ngModule:

@NgModule({
    declarations: [(...)],
    imports: [(...)],
    providers: [
        ...RxJS_Services,
        {
            provide: HTTP_INTERCEPTORS,
            useClass: HTTPListener,
            multi: true
        }
    ],
    bootstrap: [(...)]
})
export class AppModule { }

Estamos prontos para começar a exibir a mensagem/animação de carregamento.

3. Exibir a mensagem/animação

Você pode optar por diversar formas de exibir uma mensagem ou animação. Separei duas fontes legais e fáceis de implementar. A primeira é de um site chamado Free FrontEnd e são todos em CSS: https://freefrontend.com/css-loaders/. São 83 loaders diferentes para implementar diretamente usando CSS.

A segunda opção, e a que tenho utilizado, é uma biblioteca para o próprio Angular (o que faz ficar muito mais fácil) chamada ngx-spinner (demo). Antes de utilizar é preciso instalá-la. Utilizando o npm:

npm install ngx-spinner --save

Depois que tiver instalado, faça a importação do módulo no seu módulo root:

// Import library module
import { NgxSpinnerModule } from 'ngx-spinner';
 
@NgModule({
  imports: [
    // ...
    NgxSpinnerModule
  ]
})
export class AppModule { }

E adicione o serviço NgxSpinnerService nos componentes que desejar. Abaixo deixo como ficou meu componente principal do projeto, utilizando NgxSpinnerService.show() e NgxSpinnerService.hide():

import { Component } from '@angular/core';
import { HTTPStatus } from './loader/interceptor';
import { NgxSpinnerService } from 'ngx-spinner';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.sass']
})
export class AppComponent {
  constructor(private httpStatus: HTTPStatus, private spinner: NgxSpinnerService) {
    this.httpStatus.getHttpStatus().subscribe((status: boolean) => {
      if(status) {
        spinner.show();
      }
      else {
        spinner.hide();
      }
    });
  }
}

Lembre-se de no html do seu component adicionar os comandos do ngx-spinner. É possível gerar o código no próprio demo da biblioteca:

<ngx-spinner
        bdColor = "rgba(0, 0, 0, 0.5)"
        size = "large"
        color = "#fff"
        type = "ball-scale-ripple-multiple"
        fullScreen = "true"
>
    <h2 style="color: white" > Aguarde... </h2>
</ngx-spinner>

Espero que ajude. Dúvidas, sugestões e comentários, só deixar aqui embaixo.

Abraços,
Gui Mori

Related Posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

My New Stories

Keeper está decolando