Sintaxe de controle de fluxo no Angular 2026: @if, @for, @switch e perguntas de entrevista

Guia completo sobre a sintaxe de controle de fluxo do Angular com @if, @for e @switch. Aprenda a substituir as diretivas estruturais por blocos de controle modernos e prepare-se para entrevistas técnicas.

Sintaxe de controle de fluxo Angular com @if, @for e @switch

A sintaxe de controle de fluxo do Angular (@if, @for, @switch) substitui as diretivas estruturais *ngIf, *ngFor e *ngSwitch que dominaram os templates do Angular por anos. Introduzida no Angular 17, estabilizada desde o Angular 18 e tornando-se a sintaxe padrão no Angular 22, esta abordagem baseada em blocos aproxima os templates das construções de programação padrão enquanto permite melhores otimizações de performance.

Migração automática disponível

Execute ng generate @angular/core:control-flow para migrar automaticamente os templates existentes das diretivas estruturais para a nova sintaxe de controle de fluxo. Ambas as sintaxes coexistem durante a migração, mas ngIf, ngFor e ngSwitch estão soft-deprecated desde o Angular 19.

Por que o Angular substituiu as diretivas estruturais pela sintaxe de blocos

As diretivas estruturais exigiam importar CommonModule, usavam uma microsintaxe diferente do JavaScript e forçavam os desenvolvedores a envolver o conteúdo em ng-template para condições complexas. A nova sintaxe de controle de fluxo resolve esses problemas:

  • Sem imports necessários: @if, @for e @switch são integrados ao framework
  • Sintaxe similar ao JavaScript: condições e loops se leem como código padrão
  • Suporte nativo a @else: não é mais necessário referências a ng-template para branches else
  • Melhor tree-shaking: blocos de controle de fluxo não utilizados têm impacto zero no bundle

A documentação do Angular recomenda adotar completamente a sintaxe de blocos em todos os novos projetos e migrar progressivamente as bases de código existentes.

@if: Renderização condicional sem ng-template

O bloco @if renderiza conteúdo condicionalmente baseado em uma expressão truthy. Diferente do *ngIf, ele suporta branches @else if e @else diretamente:

user-status.component.tstypescript
import { Component, input } from '@angular/core';

@Component({
  selector: 'app-user-status',
  template: `
    @if (user().role === 'admin') {
      <app-admin-panel />
    } @else if (user().role === 'editor') {
      <app-editor-panel />
    } @else {
      <app-viewer-panel />
    }
  `
})
export class UserStatusComponent {
  user = input.required<{ role: string }>();
}

O bloco @if também suporta aliasing de variáveis com a palavra-chave as, que extrai valores de expressões aninhadas:

html
<!-- Evita acessos repetidos a propriedades -->
@if (user().profile?.settings?.theme; as theme) {
  <p>Tema atual: {{ theme }}</p>
}

Esse padrão é particularmente útil ao trabalhar com dados assíncronos ou objetos profundamente aninhados, já que a variável com alias só é definida quando a condição é truthy.

@for: Iteração com expressão track obrigatória

O bloco @for itera sobre qualquer iterável JavaScript, com otimizações para arrays. Diferente do *ngFor, ele exige uma expressão track:

product-list.component.tstypescript
import { Component, input } from '@angular/core';

interface Product {
  id: string;
  name: string;
  price: number;
}

@Component({
  selector: 'app-product-list',
  template: `
    @for (product of products(); track product.id) {
      <app-product-card [product]="product" />
    } @empty {
      <p>Nenhum produto disponível</p>
    }
  `
})
export class ProductListComponent {
  products = input.required<Product[]>();
}

A expressão track informa ao Angular como identificar cada item entre re-renderizações. Escolher a propriedade de tracking correta impacta diretamente a performance:

Expressão TrackCaso de usoImpacto na performance
track item.idItens com identificadores únicosÓtimo: atualizações DOM mínimas
track $indexListas estáticas que nunca reordenamAceitável: re-renderização completa ao reordenar
track itemTracking por referênciaRuim: nova referência = novo nó DOM

Variáveis contextuais em blocos @for

O Angular fornece variáveis implícitas dentro dos blocos @for que expõem metadados de iteração:

html
@for (item of items(); track item.id; let i = $index, isLast = $last) {
  <li class="item" [class.last]="isLast">
    {{ i + 1 }}. {{ item.name }}
  </li>
}

Variáveis contextuais disponíveis:

  • $index: posição base zero
  • $count: número total de itens
  • $first, $last: flags booleanas para o primeiro/último item
  • $even, $odd: flags booleanas baseadas na paridade do índice

Pronto para mandar bem nas entrevistas de Angular?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

@switch: Ramificação condicional type-safe

O bloco @switch fornece pattern matching exaustivo com comparação de igualdade estrita (===):

status-badge.component.tstypescript
import { Component, input } from '@angular/core';

type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered';

@Component({
  selector: 'app-status-badge',
  template: `
    @switch (status()) {
      @case ('pending') {
        <span class="badge badge-gray">Pendente</span>
      }
      @case ('processing') {
        <span class="badge badge-blue">Processando</span>
      }
      @case ('shipped') {
        <span class="badge badge-yellow">Enviado</span>
      }
      @case ('delivered') {
        <span class="badge badge-green">Entregue</span>
      }
    }
  `
})
export class StatusBadgeComponent {
  status = input.required<OrderStatus>();
}

Diferente do switch do JavaScript, o @switch do Angular não tem comportamento de fallthrough. Múltiplas condições apontando para o mesmo bloco exigem instruções @case consecutivas:

html
@switch (userRole()) {
  @case ('admin')
  @case ('superadmin') {
    <app-admin-dashboard />
  }
  @case ('editor')
  @case ('reviewer') {
    <app-editor-dashboard />
  }
  @default {
    <app-viewer-dashboard />
  }
}

Verificação de tipos exaustiva com @default never

O Angular 22 suporta verificação de exaustividade em tempo de compilação. Usar @default never; declara que não devem existir casos restantes:

typescript
type Theme = 'light' | 'dark' | 'system';

// Erro de compilação se um valor Theme estiver faltando nos blocos @case
@switch (theme()) {
  @case ('light') { /* ... */ }
  @case ('dark') { /* ... */ }
  @case ('system') { /* ... */ }
  @default never;
}

Adicionar um novo valor à union Theme dispara um erro de compilação, forçando os desenvolvedores a tratar todos os casos.

Integração da sintaxe de controle de fluxo com Angular Signals

Os blocos de controle de fluxo se integram perfeitamente com Angular Signals, permitindo reatividade granular:

dashboard.component.tstypescript
import { Component, computed, signal } from '@angular/core';

@Component({
  selector: 'app-dashboard',
  template: `
    @if (isLoading()) {
      <app-skeleton />
    } @else if (hasError()) {
      <app-error [message]="errorMessage()" />
    } @else {
      @for (item of filteredItems(); track item.id) {
        <app-item-card [item]="item" />
      } @empty {
        <p>Nenhum item corresponde aos seus filtros</p>
      }
    }
  `
})
export class DashboardComponent {
  items = signal<Item[]>([]);
  filter = signal('');
  isLoading = signal(true);
  hasError = signal(false);
  errorMessage = signal('');

  // O signal computed só recalcula quando as dependências mudam
  filteredItems = computed(() =>
    this.items().filter(item =>
      item.name.toLowerCase().includes(this.filter().toLowerCase())
    )
  );
}

Quando um signal é atualizado, o Angular reavalia apenas os blocos de controle de fluxo afetados. Combinado com a detecção de mudanças zoneless do Angular, isso permite renderização altamente otimizada.

Perguntas de entrevista frequentes sobre controle de fluxo do Angular

Entrevistas técnicas frequentemente testam o entendimento da sintaxe de controle de fluxo, especialmente as diferenças em relação às diretivas estruturais.

Pergunta 1: Por que track é obrigatório no @for?

A expressão track fornece ao Angular uma identidade estável para cada item. Sem ela, o Angular não consegue determinar eficientemente quais nós DOM criar, atualizar ou destruir quando a coleção muda. Tornar track obrigatório força os desenvolvedores a tomar uma decisão explícita sobre identidade, evitando as armadilhas de performance do tracking implícito por referência.

Pergunta 2: Como @if difere de *ngIf com async pipe?

Ambos podem lidar com observables, mas @if com signals fornece acesso síncrono aos valores:

typescript
// Com *ngIf e async pipe (legado)
<div *ngIf="user$ | async as user">{{ user.name }}</div>

// Com @if e signals (moderno)
@if (user(); as user) {
  <div>{{ user.name }}</div>
}

A abordagem com signals evita gerenciamento de subscriptions e se integra melhor com a detecção de mudanças do Angular.

Pergunta 3: O @switch pode substituir cadeias complexas de @if/@else if?

O @switch deve ser usado quando se compara uma única expressão com múltiplos valores discretos. Para condições booleanas complexas envolvendo diferentes expressões, @if/@else if continua sendo mais apropriado:

html
<!-- Usar @switch para uma única expressão, múltiplos valores -->
@switch (status()) {
  @case ('active') { ... }
  @case ('inactive') { ... }
}

<!-- Usar @if para múltiplas expressões -->
@if (isAdmin() && hasPermission('write')) {
  ...
} @else if (isEditor()) {
  ...
}

Pergunta 4: O que acontece com @for quando a coleção está vazia?

Quando o iterável não contém itens, o Angular pula completamente o conteúdo do @for. O bloco opcional @empty renderiza conteúdo de fallback neste caso. Sem @empty, nada é renderizado, o que difere de alguns frameworks que renderizam um container vazio.

Migração das diretivas estruturais para controle de fluxo

O CLI do Angular fornece um schematic de migração automatizado:

bash
# Migrar projeto inteiro
ng generate @angular/core:control-flow

# Migrar diretório específico
ng generate @angular/core:control-flow --path=src/app/features

O schematic lida com a maioria das transformações automaticamente, mas é necessário revisar a saída para casos especiais:

Diretiva estruturalEquivalente controle de fluxo
*ngIf="condition"@if (condition) { }
*ngIf="condition; else elseBlock"@if (condition) { } @else { }
*ngFor="let item of items"@for (item of items; track item) { }
*ngFor="let item of items; index as i"@for (item of items; track item; let i = $index) { }
[ngSwitch] + *ngSwitchCase@switch + @case

Após a migração, remova os imports de CommonModule dos componentes standalone que não usam mais diretivas estruturais.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Pontos-chave sobre a sintaxe de controle de fluxo do Angular

  • A sintaxe de controle de fluxo do Angular (@if, @for, @switch) é a abordagem recomendada desde o Angular 19, substituindo as diretivas estruturais
  • A expressão track no @for é obrigatória: use identificadores únicos como item.id para performance ideal, evite track item que causa re-renderizações baseadas em referência
  • @if suporta branches nativos @else if e @else sem ng-template, e a palavra-chave as cria alias para valores truthy para reutilização
  • @switch usa igualdade estrita e não tem fallthrough: instruções @case consecutivas apontam para o mesmo bloco, e @default never; habilita verificação de tipos exaustiva
  • Os blocos de controle de fluxo se integram com Signals para reatividade granular: apenas os blocos afetados são re-renderizados quando signals são atualizados
  • Execute ng generate @angular/core:control-flow para migrar automaticamente os templates existentes
Desafio do dia

Você saberia encontrar o bug em Angular?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 3 de setembro de 2026

Tags

#angular
#control-flow
#templates
#signals

Compartilhar

Artigos relacionados