Desenvolver uma aplicação do mundo real com múltiplas chamadas para o servidor pode estar cheio de bugs. Se você está aqui, isso significa que você tem lutado com o atraso das chamadas de API. Estes atrasos podem causar um UX negativo. Hoje, nós vamos entender os Resolvedores de Rotas no Angular 8. Há várias coisas diferentes que podemos fazer para melhorar a experiência do usuário, como exibir um indicador de progresso. Para ficar no tópico, vamos ver o que é um Resolvedor de Rota e o que podemos alcançar com ele.
O que é um Resolvedor de Rota Angular?
Um Resolvedor é uma classe que implementa a interface Resolver do Roteador Angular. Na verdade, Resolver é um serviço que tem que estar no módulo raiz. Basicamente, um Resolver age como middleware, que pode ser executado antes de um componente ser carregado.
Você também pode gostar: Resolvedores Angulares.
Porquê Usar Resolvedores de Rota em Angular?
Para explicar como um resolvedor pode ser usado em Angular, vamos pensar no cenário quando você estiver usando *ngIf="some condition"
, e sua lógica depende do comprimento de um array, que é manipulado quando uma chamada API é completada. Por exemplo, você pode querer exibir em um componente os itens desse array que foram buscados em uma lista não ordenada.
<ul><li *ngFor="let item of items">{{item.description}}</li></ul>
Nesta situação, você pode entrar em um problema porque seus dados aparecerão depois que o componente estiver pronto. Os itens do array ainda não existem realmente. Aqui, o Route Resolver vem a calhar. A classe Route Resolver da Angular irá buscar seus dados antes que o componente esteja pronto. Suas declarações condicionais funcionarão suavemente com o Resolver.
Interface de Resolução
Antes de começarmos a implementar um Resolver de Rotas, vamos primeiro ver como a interface do Resolver.
export interface Resolve<T> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T { return 'Data resolved here...' }}
Se você quiser criar um Resolver de Rotas, você tem que criar uma nova classe que irá implementar a interface acima. Esta interface nos fornece uma função resolve
, que lhe dá dois parâmetros caso você precise deles. O primeiro é a rota, que é do tipo ActivatedRouteSnapshot
, e o segundo é o estado, do tipo RouterStateSnapshot
. Aqui, você pode fazer uma chamada API que irá obter os dados que você precisa antes que seu componente seja carregado.
Via o parâmetro route, você pode obter parâmetros de route que podem ser usados na chamada API. O método resolve
pode retornar um Observable
, uma promessa ou apenas um tipo personalizado.
Note: É importante mencionar que apenas os dados resolvidos serão retornados através deste método.
Este é um erro que muitas pessoas cometerão ao usar resolvedvers pela primeira vez! Então você precisa completá-los antes de enviá-los no roteador.
Enviar dados para o roteador através de um Resolver
Deixe me mostrar o que eu quero dizer na seção acima. Digamos que você tem um método que retorna um observável:
items: any = ;getData(): Observable<any> { const observable = Observable.create(observer => { observer.next(this.items) }); return observable;}
Então, agora, você verá que a assinatura que temos, nunca chega. Isto é causado porque você não está enviando os dados corretamente. Você não está completando a assinatura. Para corrigir esse erro, você precisa completar a assinatura adicionando mais uma linha de código.
observer.complete();
Caso você esteja retornando uma promessa, você precisa resolvê-la antes de enviar os dados para o roteador.
Implementando um resolvedor de rota
Após terminarmos com a explicação básica e por que e como usar um resolvedor de rota, vamos começar a implementar um. Neste pequeno exemplo, eu assumo que você está familiarizado com os comandos CLI da Angular e como criar um novo projeto, então eu irei demonstrar apenas o código obrigatório. Você pode encontrar o projeto demo através do repositório GitHub no final do artigo.
Neste exemplo, eu usarei o jsonplaceholder como uma API falsa para buscar dados de usuário para demonstrar chamadas de API com resolvedores de rotas.
Primeiro de tudo, vamos precisar de um serviço que irá buscar os dados do usuário para nós. Neste serviço, temos uma função chamada getUsers()
que retorna um observável.
@Injectable({providedIn: 'root'})export class FakeApiService { constructor(private http: HttpClient) { } private usersEndpoint = "https://jsonplaceholder.typicode.com/users"; getUsers(): Observable<any> { // We do not subscribe here! We let the resolver take care of that... return this.http.get(this.usersEndpoint); }}
É importante não subscrever a função getUsers. O resolvedor de rotas chamado UserResolver cuidará disso para você. O próximo passo é criar um novo serviço chamado UserResolver que irá implementar a função Resolver interface do roteador.
@Injectable({providedIn: 'root'})export class UserResolverService implements Resolve<any> { constructor(private fakeApi: FakeApiService) { } resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { return this.fakeApi.getUsers().pipe( catchError((error) => { return empty(); }); ); }}
Este serviço, UserResolver
, irá subscrever automaticamente o getUsers
observável e fornecer o roteador com os dados buscados. No caso de um erro, ao buscar os dados, você pode enviar um observável vazio e o roteador não irá prosseguir para a rota.
A navegação será terminada neste ponto. Este último passo é para criar um componente que será chamado quando o usuário for para a rota /users
. Tipicamente, sem um Resolver, você precisará buscar os dados no gancho ngOnInit
do componente e lidar com os erros causados por ‘não existem dados’. O componente do usuário é simples. Ele apenas obtém os dados do usuário no ActivatedRoute
e os exibe em uma lista não ordenada.
Após ter criado o componente do usuário, você precisa definir as rotas e dizer ao roteador para usar um resolvedor ( UserResolver
). Isto poderia ser conseguido com o seguinte código no app-routing.modulte.ts
.
const routes: Routes = ;@NgModule({ imports: , exports: })export class AppRoutingModule { }
Você precisa definir a propriedade resolver na rota do usuário e declarar o UserResolver
. Os dados serão passados para um objeto com uma propriedade chamada usuários. Depois disso, você está quase pronto. Há apenas uma coisa que você precisa fazer. Você deve obter os dados buscados no componente do usuário através do ActivatedRoute
com o seguinte código.
constructor(private activatedRoute: ActivatedRoute) { }users: any;ngOnInit() { this.activatedRoute.data.subscribe((data: { users: any }) => { this.users = data.users; });}
>Então, você pode simplesmente exibi-los em HTML sem nenhuma *ngIf
instruções ( *ngIf="users && users.length > 0
) porque os dados estarão lá antes do componente ser carregado.
<h2>Fetched Users:</h2><ul><li *ngFor="let user of users">{{ user.name }}</li></ul>
Sumário
Fechando este artigo sobre Resolvedores de Rotas, eu gostaria de apontar mais uma vez as vantagens que eles nos proporcionam. Primeiro evitamos as checagens incômodas que devem ser feitas no nível do HTML para que não tenhamos problemas até que nossos dados sejam recebidos. Em segundo lugar, eles nos permitem focar mais na experiência do usuário, pois podemos parar de navegar se ocorrer um erro nos dados enquanto os buscamos, sem ter que carregar o componente de visualização. Também podemos fazer mais antes de completar a navegação, como anexar mais lógica ou mapear os dados antes de acessar o componente carregado.
O código demo está disponível em GitHub.
Outra Leitura
- Tutorial Angular: Angular 7 e o RESTEasy Framework.
- Construindo uma Aplicação Angular 5 Passo a Passo.
- Aplicação Angular 7 + Bota de Mola: Olá Mundo Exemplo.