← Angular: componentes, reatividade e decisões de aplicação
07 / 10 · 65 MIN

Routing, estado e decisões de navegação

Interpretar matching, parâmetros e decisões de navegação com estado que pode mudar na mesma instância. Definir carregamento, saída e falha sem confundir controlo local de rotas com autorização remota.

Matching e ordem explícita

Angular usa a primeira correspondência aplicável no array de rotas. Uma rota específica como orders/new deve aparecer antes de orders/:id para que new não seja capturado como id. O wildcard ** fica depois das rotas que devem ter oportunidade de corresponder.

Títulos e nomes das classes não corrigem uma ordem inadequada. No exemplo, cada rota declara title para atualizar o título da página quando ativa. A vista de rota desconhecida é parte da aplicação; definir um status HTTP adequado para uma resposta do servidor é uma responsabilidade adicional da entrega.

Path, query e identidade da seleção

O path orders/:id declara um segmento de identidade. Em /orders/R7?tab=history, id pertence aos parâmetros da rota e tab à query. A query é adequada para estados como filtros, ordenação e paginação, mas deve ser validada antes de ser usada como regra de negócio.

Para alterar sort preservando page, queryParamsHandling: merge aplica a atualização aos parâmetros existentes. Preservar parâmetros tem uma intenção concreta; não significa transportar indiscriminadamente estado sensível entre todas as páginas. Decide quais valores fazem parte do endereço partilhável do recurso.

Snapshot ou observação reativa

Um snapshot representa o estado num instante. Se guardares o id numa variável durante a construção, essa variável não passa a seguir navegações futuras. Uma instância reutilizada entre R7 e R8 precisa de observar parâmetros atualizados quando o contrato exige acompanhar a seleção.

O exemplo usa paramMap e AsyncPipe, produzindo o texto para cada valor observado. Se houver carregamento remoto, compõe essa fonte com uma política de aceitação apropriada, como a substituição de leituras antigas. Alterar readonly ou o nome da variável não cria uma ligação reativa.

Navegação relativa e vistas aninhadas

RouterLink permite ligações declarativas e Router.navigate permite comandos de navegação em código. Com relativeTo a apontar para /orders/R7, o comando edit sem barra inicial pode resolver /orders/R7/edit. Uma barra inicial torna o comando absoluto e ignora esse contexto relativo.

Para rotas children, o componente pai precisa de RouterOutlet para apresentar a vista filha no lugar pretendido. A árvore de configuração e a árvore de apresentação têm de concordar. Não interpretes um outlet ausente como prova de que o URL ou o servidor rejeitou o acesso.

Guards devolvem decisões

CanActivate decide acesso à rota no cliente; CanDeactivate pode consultar a instância atual antes de sair, por exemplo perante um draft pendente. CanMatch false tem uma consequência específica: permite tentar outras rotas correspondentes. Para redirecionar, devolve UrlTree ou RedirectCommand em vez de false seguido de outra navegação imperativa.

Um resultado Observable usa a primeira emissão e depois a subscrição termina. Nenhum destes contratos garante autorização de pedidos feitos diretamente à API. A aplicação deve definir uma verificação no servidor para cada operação e recurso protegido.

Concorrência dos guards e dependências

Um array de guards estabelece ordem de início e prioridade de resultados, mas não garante que um guard assíncrono termine antes de o seguinte começar. Se checkRole depende de dados carregados por loadUser, colocar loadUser primeiro não basta para provar a dependência.

Junta a sequência num guard que a controla ou usa um serviço com obtenção coordenada partilhada. Esta decisão difere de apenas acrescentar um atraso para tentar que os dados cheguem. A revisão deve identificar quando a informação necessária fica disponível e qual resultado tem prioridade quando há decisões diferentes.

Resolvers, espera e falha observável

Um resolver obtém dados antes da ativação e torna-os acessíveis no estado da rota. Isto pode evitar um componente sem dados obrigatórios, mas desloca a espera para a navegação. Considera feedback durante essa espera e limita a quantidade de trabalho obrigatório.

Uma falha sem tratamento pode produzir NavigationError; define tratamento centralizado, tratamento de eventos ou uma decisão explícita no resolver. Devolver um objeto vazio para fazer desaparecer o erro só é correto se esse objeto satisfizer realmente o contrato.

Não comunica sucesso de carregamento quando os dados necessários continuam ausentes.

// Reading example: local routes and displayed identity; no application bootstrap.
import {AsyncPipe} from '@angular/common';
import {ApplicationConfig, Component, inject} from '@angular/core';
import {ActivatedRoute, RouterLink, RouterOutlet, Routes, provideRouter} from '@angular/router';
import {map} from 'rxjs';
@Component({selector: 'new-order', standalone: true, template: '<h1>New order</h1>'})
export class NewOrder {}
@Component({
  selector: 'order-detail', standalone: true, imports: [AsyncPipe],
  template: `<h1>Order {{ id$ | async }}</h1>`,
})
export class OrderDetail {
  private readonly route = inject(ActivatedRoute);
  readonly id$ = this.route.paramMap.pipe(map(params => params.get('id') ?? 'missing'));
}
@Component({selector: 'unknown-route', standalone: true, template: '<h1>Unknown route</h1>'})
export class UnknownRoute {}
export const routes: Routes = [
  {path: 'orders/new', component: NewOrder, title: 'Create an order'},
  {path: 'orders/:id', component: OrderDetail, title: 'Order details'},
  {path: '**', component: UnknownRoute, title: 'Unknown route'},
];
export const appConfig: ApplicationConfig = {providers: [provideRouter(routes)]};
@Component({
  selector: 'order-app', standalone: true, imports: [RouterLink, RouterOutlet],
  template: `<nav>
    <a routerLink="/orders/new">New</a>
    <a [routerLink]="['/orders', 'R7']">R7</a>
    <a [routerLink]="['/orders', 'R8']">R8</a>
  </nav><router-outlet />`,
})
export class OrderApp {}
NA PRÁTICA

orders/new precede orders/:id, pelo que new não é interpretado como o identificador nesse percurso. OrderDetail acompanha paramMap através de AsyncPipe em vez de guardar apenas a leitura inicial. A página só apresenta a identidade recebida; não obtém uma encomenda nem confirma autorização.

O wildcard apresenta uma vista local e não define por si só um status HTTP do servidor.

Armadilhas comuns

Colocar wildcards antes de rotas específicas; confundir query com path; esperar atualizações de uma variável capturada do snapshot; combinar false com navegação imperativa para redirect; depender da conclusão sequencial de guards async; transformar erro de resolver em sucesso vazio.

Tópicos relacionados: Componentes, templates e identidade da vista · HTTP, streams e aceitação de respostas · Fronteiras de segurança e conteúdo confiável

Leva esta ideia contigo

A configuração define como encontrar a rota, mas o estado observado e a política de navegação evoluem no tempo. Faz corresponder dados à identidade atual, torna explícita a espera por dados e trata falhas como resultados distintos. Guards continuam a ser decisões no cliente.

Criar conta

Referência: Route matching order · Angular 22; standalone components