[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:
- Criar o interceptador de requisições HTTP
- Implementar o Listener no módulo principal
- 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