Angular Resolver é um antipadrão?

Andrew Rosário
5 min readMar 10, 2022

--

O Angular disponibiliza uma série de ferramentas para lidar com a navegação por rotas. Um desses recursos é o Resolver, que tem como objetivo interceptar uma rota antes que um componente seja iniciado. Podemos por exemplo executar uma operação assíncrona como uma requisição para uma API. Quando a requisição for finalizada, podemos obter estes dados resolvidos diretamente na inicialização do componente.

Seu uso é bastante útil em diversos cenários. Veja um exemplo onde realizamos uma requisição no Resolver e capturamos seus dados no componente.

No módulo de rotas referente ao componente, vinculamos o nosso Resolver com a chave resolve.

E em nosso componente podemos capturar estes dados diretamente em sua inicialização de forma síncrona. É uma solução que acaba sendo muito confortável, principalmente para desenvolvedores que sofrem com a manipulação de Observables.

Problemas com UX

Porém ao utilizar Resolvers podemos ter um grande problema de User Experience (UX) em nossa aplicação. Imagine um cenário onde o usuário clicou em uma rota que possui um Resolver. Neste momento uma requisição HTTP foi iniciada, mas ela pode demorar alguns segundos para ser finalizada, principalmente se a conexão do usuário não for muito boa.

Se a requisição demorar 7 segundos, serão 7 segundos sem nenhum tipo de resposta para o usuário. Neste período o usuário impaciente provavelmente terá clicado no mesmo link umas 3 vezes tentando entender porque nada acontece.

Este comportamento acaba indo contra a ideia de uma Single Page Application (SPA). Aplicações modernas têm o intuito de reagir imediatamente perante as ações do usuário, pois os dados já estão no lado do cliente.

Portanto muitas pessoas acabam questionando o uso do Resolver, denominando-o como um antipadrão. Existe um tópico bastante discutido no Stack Overflow sobre isso. Ferdinand Malcher comenta que Resolvers só devem ser utilizados para fins acadêmicos.

Mas qual a solução?

Como dito anteriormente, Resolvers podem ser muito úteis para lidar com dados assíncronos, porém não temos uma resposta para o usuário. A solução mais viável seria lidar com essas requisições assíncronas diretamente no componente.

O Angular recomenda fortemente que utilizemos a programação reativa com o RxJS. É sabido também que ela pode ser bastante complexa para algumas pessoas, principalmente para iniciantes que nunca lidaram com este paradigma. Mas a verdade é que este é o melhor caminho, tanto em questões de usabilidade como escalabilidade de uma aplicação.

Em vez de utilizarmos um Resolver para trazer os dados da requisição HTTP, podemos executar um subscribe no serviço diretamente no componente.

Aqui temos um ponto de atenção. O componente será inicializado imediatamente, porém não temos controle de quando a requisição HTTP será finalizada. Enquanto ela não finalizar não teremos valor nenhum na variável user. Como estamos utilizando ela no template HTML, tomaremos um erro dizendo que não existe a propriedade name dentro de undefined.

São nesses momentos que as pessoas começam a fugir de Observables! Algumas manipulações devem ser feitas. Já com Resolvers isso não seria necessário.

Mas calma! O Angular disponibiliza algumas soluções para estas situações. Pensando mais no lado reativo, temos o AsyncPipe. É um Pipe muito poderoso do framework. Ele é capaz de se inscrever em um Observable diretamente no Template HTML e ainda por cima realiza a desinscrição do mesmo quando o componente é destruído. Você pode ler mais sobre o AsyncPipe neste artigo.

Para dar alguma informação para o usuário de que os dados estão sendo carregados podemos criar uma flag de loading. Vinculamos a ela um componente de Spinner, por exemplo. Ou então utilizamos Ghost Elements (ou Skeleton) que considero ainda mais interessante.

Componente “Skeleton” do NG-ZORRO

E a medida que vamos avançando nos conceitos do Angular, percebemos que quase tudo tem um toque do RxJS. Formulários reativos, navegação por rotas, requisições HTTP, gerenciamento de estado e muitos outros pontos.

Portanto recomendo fortemente que o estudo do RxJS seja um dos pontos principais. O começo pode ser um pouco complicado (o que é normal), mas lá na frente você será recompensado. Chegará um momento em que você enxergará Observables em toda parte. E isso é uma coisa boa!

Mas então os Resolvers nunca devem ser utilizados?

Diferente de algumas pessoas, eu não acredito que o Resolver nunca deve ser utilizado. Até porque se ele fosse tão ruim, a equipe do Angular não o teria criado.

Particularmente eu gosto da ideia de um Resolver na questão da separação de responsabilidades. Nossos componentes acabam ficando mais limpos, uma vez que podemos ter uma dependência a menos. O grande problema é a UX, mas podemos contornar essa situação com o que o Angular nos oferece.

Router Events

Uma vez que injetamos o Router em nossos componentes, temos total controle de todos os acontecimentos referentes a navegação por rotas. Temos o Observable events que nos informa qual o tipo de navegação está acontecendo no momento. Os eventos que nos interessam são: ResolverStart e ResolverEnd. Os nomes são auto-explicativos. Conseguimos saber quando um Resolver iniciou e quando terminou.

A ideia então é criar uma espécie de Progress Bar Global que irá aparecer sempre que um Resolver for iniciado. Você provavelmente já deve ter visto essa Progress Bar em alguma aplicação web por aí.

Para implementá-la, injetamos o Router no AppComponent e fazemos a manipulação dos streams. Criamos um primeiro Observable para mapear como true quando a navegação for uma instância de ResolveStart. O segundo Observable vai mapear como false quando a navegação for uma instância de ResolveEnd. Por fim criamos um terceiro Observable chamado isLoading$ que vai ouvir os dois streams anteriores a partir da função merge.

E aqui ressalto mais uma vez a importância do RxJS. Perceba a quantidade de recursos utilizados da programação reativa. É o chamado “Observables everywhere”!

Você pode conferir este exemplo pelo StackBlitz:

Extra: Utilizando Resolvers para alterar o título da página

A versão 14 do Angular trouxe algumas novidades para o sistema de roteamento. Uma delas é possibilidade de alterar o título de uma rota diretamente na sua declaração. Este é um aspecto muito importante para a acessibilidade de uma aplicação.

E caso você precise capturar o título de uma página de forma assíncrona, então um Resolver pode ser utilizado para essa tarefa. Basta retornar uma Promise ou um Observable que traga a string do título da página. Eis então mais um caso de uso útil para os Resolvers!

Conclusão

Resolvers são úteis para manipular dados assíncronos, porém devemos utilizá-los com precaução, uma vez que podemos ter um grande problema de UX. Apesar de termos algumas boas soluções como o controle de Router Events, é preferível utilizar Observables para o controle desses dados. Ter um bom conhecimento de RxJS facilita e muito a nossa vida nesses casos!

--

--

Andrew Rosário
Andrew Rosário

Written by Andrew Rosário

Desenvolvedor Front-end, mentor e palestrante. Apaixonado por tecnologia e por compartilhar conhecimento.

Responses (2)