DEV Community

Migrando um YMS de 5 anos: Do Angular 16 ao 21 (Parte 4) - Templates Modernos: O Adeus ao *ngIf e *ngFor e a mágica do @defer

Fala, comunidade dev! 👋

No artigo anterior desta nossa jornada de migração de um sistema YMS (do Angular 16 para o 21), exploramos como a função inject() limpou as nossas classes TypeScript e resolveu o "inferno" dos construtores. Mas hoje, vamos fechar os arquivos .ts e olhar para os nossos templates HTML.

O Angular passou por uma mudança drástica na forma como renderizamos o HTML. É hora de dar o adeus definitivo às velhas diretivas estruturais (*ngIf, *ngFor, *ngSwitch) e abraçar a nova sintaxe de Control Flow e o poderoso bloco @defer.


1. O Adeus ao CommonModule e à sintaxe pesada

Durante anos, para fazer um simples if/else no Angular, precisávamos importar o CommonModule (ou as diretivas de forma isolada nos componentes Standalone) e lidar com a criação de <ng-template> para blocos else.

Em uma tela de listagem de motoristas, o código clássico era assim:

O Antes (Angular 16):

<div *ngIf="motoristas.length > 0; else semMotoristas">
  <ul>
    <li *ngFor="let motorista of motoristas; trackBy: trackPorId">
      {{ motorista.nome }}
    </li>
  </ul>
</div>

<ng-template #semMotoristas>
  <p>Nenhum motorista encontrado na fila.</p>
</ng-template>
Enter fullscreen mode Exit fullscreen mode

2. O Novo Control Flow: Limpo, Nativo e Direto

A partir do Angular 17, o framework introduziu uma nova sintaxe de bloco embutida diretamente no compilador. Não precisamos importar mais nada para fazer verificações lógicas. O código aproxima-se muito da fluidez de motores de template de outras linguagens (como o Razor ou o Blade).

Olhem como o mesmo cenário acima fica agora, no Angular 21:

O Depois (Angular 21):

@if (motoristas().length > 0) {
  <ul>
    @for (motorista of motoristas(); track motorista.id) {
      <li>{{ motorista.nome }}</li>
    }
  </ul>
} @else {
  <p>Nenhum motorista encontrado na fila.</p>
}
Enter fullscreen mode Exit fullscreen mode

As grandes vitórias aqui:

  1. Menos Imports: O @if e o @for são nativos, reduzindo o boilerplate do componente.
  2. Track Obrigatório: O @for obriga a utilização da cláusula track. Isto previne um dos erros de performance mais comuns em projetos legados: renderizar listas gigantes de grids dinâmicas sem identificar os itens de forma unívoca, o que causava re-renderizações desnecessárias.
  3. Leitura Fluida: Acabaram os <ng-template> perdidos no final do arquivo apenas para gerenciar estados de else ou loading.

3. A Verdadeira Mágica em Sistemas Enterprise: O @defer

Se o Control Flow melhora a Developer Experience (DX), o bloco @defer é um divisor de águas absoluto para a User Experience (UX) e Web Performance.

Num sistema logístico complexo, temos telas que carregam gráficos pesados de dashboards, modais com mapas de pátios e componentes complexos do PrimeNG que só são usados se o usuário interagir com eles. Antigamente, dividir o carregamento destes componentes exigia código complexo e Lazy Loading manual.

Agora, o Angular faz isso diretamente no HTML de forma declarativa.

O Exemplo Prático:
Imaginem um componente muito pesado de "Mapa de Pátio Logístico" que só deve ser carregado pelo navegador do usuário quando ele fizer scroll para baixo na página e o componente entrar na área visível (viewport):

@defer (on viewport) {
  <app-mapa-patio-pesado [dados]="dadosPatio"></app-mapa-patio-pesado>
} @placeholder {
  <p-skeleton width="100%" height="400px"></p-skeleton>
} @loading {
  <div class="spinner-logistico">Carregando o mapa...</div>
}
Enter fullscreen mode Exit fullscreen mode

O Esbuild/Vite (o nosso novo bundler) entende este @defer durante o build, corta o componente app-mapa-patio-pesado e coloca-o num arquivo .js separado. Este pedaço de código só trafega na rede quando o usuário precisa realmente dele. Reduzimos dezenas de megabytes no carregamento inicial da aplicação com algumas linhas de HTML!


Conclusão

Atualizar o HTML de um projeto com 5 anos de idade assusta, mas felizmente a equipe do Angular disponibilizou esquemas de migração automatizados (ng generate @angular/core:control-flow) que fazem o trabalho pesado por nós.

Os novos templates deixaram as nossas views mais legíveis e deram-nos um controle de Lazy Loading granular sem paralelo.

No próximo artigo (Parte 5), vamos unir o HTML limpo com um estado TypeScript impecável. Vamos falar de reatividade, do adeus ao velho ngOnDestroy e mergulhar fundo no uso de Signals!

Já utilizaram o comando de migração automática de HTML nos projetos de vocês? Como foi a experiência de refatorar os antigos *ngIf e *ngFor? Compartilhem nos comentários! 👇

Top comments (0)