Engenharia de IA Aplicada à Programação Web
Aula 2 de 10
Navegar por módulos e aulas
  1. 01 Fundamentos da Web
  2. 02 JavaScript moderno
  3. 03 TypeScript, Git e qualidade
  4. 04 Angular
  5. 05 Node.js, NestJS e APIs
  1. 4.1 Introdução ao Angular e componentes
  2. 4.2 Templates, bindings e control flow
  3. 4.3 Composição, inputs e outputs
  4. 4.4 Serviços e injeção de dependência
  5. 4.5 Rotas, layouts e navegação
  6. 4.6 Formulários e validação
  7. 4.7 HTTP, APIs e RxJS
  8. 4.8 Signals e estado da interface
  9. 4.9 Interceptors, erros e acessibilidade
  10. 4.10 Testes, compilação e projeto final

Módulo 4 · Aula 4.2

Templates, bindings e control flow

2 horas Teoria + laboratório Angular
Ver fonte Markdown

Identificação

Introdução

O que é um template Angular?

Template é a descrição da view de um componente. Ele se baseia em HTML e acrescenta sintaxes para conectar dados, propriedades, eventos e estruturas condicionais.

<h2>{{ title }}</h2>
<button [disabled]="tasks().length === 0">Limpar</button>

Angular compila o template. O navegador não interpreta {{ }}, [ ], ( ) ou @if por conta própria.

Template Angular é HTML puro?

Não. Grande parte do HTML continua válida, mas alguns caracteres possuem significado especial:

  • {{ expression }}: interpolação de texto;
  • [property]="expression": property binding;
  • [attr.name]="expression": attribute binding;
  • [class.name]="condition": class binding;
  • (event)="handler()": event binding;
  • @if, @for, @switch: control flow.

O que é binding?

Binding é uma conexão dinâmica entre o estado do componente e seu template. Quando o estado utilizado muda, Angular atualiza a parte relacionada da interface.

Nem todo valor no HTML é binding:

<button type="button">Salvar</button>

type="button" é estático.

<button [disabled]="saving()">Salvar</button>

disabled depende do estado.

Property e attribute são a mesma coisa?

Não. O HTML inicial possui atributos; o navegador cria objetos DOM com propriedades. Em Angular, [disabled] altera a propriedade do botão. Para atributos sem propriedade equivalente ou para ARIA, usamos binding de atributo:

<button [attr.aria-pressed]="selected()">

O que é control flow?

É a sintaxe que decide quais partes do template serão criadas:

@if (tasks().length > 0) {
  <p>Existem tarefas.</p>
} @else {
  <p>Nenhuma tarefa.</p>
}

Ela se parece com estruturas do JavaScript, mas pertence ao compilador de templates do Angular.

@for é igual ao for do JavaScript?

Os dois repetem, mas rodam em contextos diferentes. @for produz e atualiza views. Ele exige uma expressão track para relacionar cada item ao elemento correspondente.

@for (task of tasks(); track task.id) {
  <li>{{ task.title }}</li>
}

Por que track importa?

Sem identidade estável, o framework teria dificuldade para saber qual item mudou, foi removido ou apenas mudou de posição. task.id permite preservar a relação entre dado e DOM e reduzir operações desnecessárias.

Não use $index para listas que podem ser reordenadas, inseridas ou removidas quando existe um identificador real.

O que é @empty?

É o bloco exibido quando o @for não possui itens:

@for (task of tasks(); track task.id) {
  <app-task-card />
} @empty {
  <p>Nenhuma tarefa encontrada.</p>
}

Como isso entra na Knowledge AI?

Criaremos uma lista que apresenta tarefas, permite alternar conclusão, filtrar o resultado e mostra um estado vazio acessível.

Objetivos

Ao final da aula, o aluno deverá conseguir:

  1. explicar o que é template Angular;
  2. diferenciar HTML estático e binding;
  3. utilizar interpolação;
  4. criar property bindings;
  5. criar attribute bindings;
  6. aplicar class e style bindings;
  7. responder a eventos;
  8. utilizar $event com cuidado;
  9. criar condições com @if;
  10. repetir itens com @for;
  11. escolher uma expressão track estável;
  12. representar lista vazia com @empty.

Pré-requisitos

  • Aula 4.1 concluída;
  • componentes, classe TypeScript e template;
  • arrays, eventos e funções.

Pergunta orientadora

Como declarar uma interface dinâmica sem manipular manualmente cada elemento do DOM?

Roteiro sugerido

Etapa Duração
Template e expressões 20 min
Bindings 35 min
Eventos 20 min
Control flow 30 min
Laboratório 10 min
Revisão 5 min

1. Template externo e inline

Externo:

@Component({
  templateUrl: "./task-list.html",
})

Inline:

@Component({
  template: `<h2>{{ title }}</h2>`,
})

Templates pequenos podem ser inline. Arquivos separados ajudam quando a view cresce.

2. Interpolação

<h2>{{ heading }}</h2>
<p>{{ tasks().length }} tarefas</p>

O resultado é convertido em texto. Angular trata a interpolação como binding, não como concatenação manual de HTML.

3. Interpolação não é innerHTML

<p>{{ userText }}</p>

O valor é apresentado como texto. Não use [innerHTML] para contornar isso com conteúdo não confiável. HTML dinâmico exige análise de segurança específica.

4. Expressões de template

Templates permitem leitura de propriedades, chamadas, operadores e expressões controladas:

<p>{{ task.title.toUpperCase() }}</p>

Evite cálculos caros e lógica de negócio extensa. Extraia para estado derivado, métodos simples ou serviços.

5. Signals no template

Nesta aula, o estado mutável será guardado em signals:

tasks = signal<Task[]>(initialTasks);

No template, lemos chamando:

<p>{{ tasks().length }}</p>

Signals serão aprofundados na Aula 4.8. Aqui eles apenas fornecem estado reativo para praticar bindings.

6. Property binding

<button [disabled]="tasks().length === 0">
  Limpar
</button>

Os colchetes informam que o valor é uma expressão e será escrito na propriedade DOM ou em um input de componente.

Sem colchetes:

<button disabled="false">

O atributo continua presente e o botão pode permanecer desabilitado. Booleanos não devem ser representados como texto dessa forma.

7. Attribute binding

<section [attr.aria-label]="listLabel()">

Use attr. quando precisar controlar um atributo. Se o valor resultar em null, o Angular remove o atributo.

8. ARIA binding

<button
  type="button"
  [attr.aria-pressed]="showCompleted()"
>
  Mostrar concluídas
</button>

Binding não substitui HTML semântico. Um botão continua precisando ser um <button>.

9. Class binding

<article [class.is-done]="task.status === 'done'">

A classe é adicionada quando a expressão é truthy e removida quando falsy.

Também é possível vincular class a string, array ou objeto, mas prefira a forma mais legível para o caso.

10. Style binding

<progress
  [style.--completion]="completion() + '%'"
></progress>

Para valores numéricos com unidade:

<div [style.width.%]="completion()"></div>

Não use estilos inline para substituir toda a arquitetura CSS.

11. Event binding

<button type="button" (click)="toggleTask(task.id)">
  Alternar
</button>

Os parênteses conectam um evento a uma expressão. O método fica na classe do componente.

12. O objeto $event

<input (input)="updateQuery($event)">

$event representa o evento. No TypeScript, valide o target:

updateQuery(event: Event): void {
  if (event.target instanceof HTMLInputElement) {
    this.query.set(event.target.value);
  }
}

Não use assertions sem verificar o elemento real.

13. Evitar manipuladores complexos no HTML

Ruim:

<button (click)="tasks.set(tasks().filter(...).map(...))">

Melhor:

<button (click)="clearCompleted()">

O template comunica intenção e a classe implementa a operação testável.

14. @if

@if (loading()) {
  <p>Carregando...</p>
} @else {
  <app-task-list />
}

O bloco controla se aquela view existe. Não é apenas esconder com CSS.

15. @else if e @else

@if (error()) {
  <p role="alert">{{ error() }}</p>
} @else if (tasks().length === 0) {
  <p>Nenhuma tarefa.</p>
} @else {
  <app-task-list />
}

Ordene condições do estado mais específico para o conteúdo normal.

16. Alias em @if

@if (selectedTask(); as task) {
  <h2>{{ task.title }}</h2>
}

O alias evita repetir uma expressão longa e restringe o valor ao bloco.

17. @for

@for (task of visibleTasks(); track task.id) {
  <article>
    <h3>{{ task.title }}</h3>
  </article>
}

O bloco cria uma view por item e usa task.id para acompanhar sua identidade.

18. Escolhendo track

Preferência:

identificador único e estável → task.id
lista realmente estática → $index
último recurso → referência do item

O valor não deve mudar durante a vida lógica do item.

19. Variáveis contextuais

@for (
  task of visibleTasks();
  track task.id;
  let position = $index, last = $last
) {
  <p>Tarefa {{ position + 1 }}</p>
}

Também existem $count, $first, $even e $odd.

20. @empty

@for (task of visibleTasks(); track task.id) {
  <app-task-card />
} @empty {
  <p>Nenhuma tarefa corresponde ao filtro.</p>
}

O bloco deve aparecer imediatamente após o conteúdo do @for.

21. @switch

@switch (task.status) {
  @case ("todo") { <span>A fazer</span> }
  @case ("doing") { <span>Em andamento</span> }
  @case ("done") { <span>Concluída</span> }
  @default { <span>Status desconhecido</span> }
}

Não existe fallthrough como no switch tradicional.

22. Estado derivado

visibleTasks = computed(() => {
  return this.tasks().filter((task) => {
    return this.showCompleted() || task.status !== "done";
  });
});

O template lê visibleTasks() sem repetir a regra de filtro.

23. Atualização imutável

toggleTask(id: number): void {
  this.tasks.update((tasks) =>
    tasks.map((task) =>
      task.id === id
        ? { ...task, status: task.status === "done" ? "todo" : "done" }
        : task,
    ),
  );
}

Uma nova referência torna a mudança explícita e preserva outros itens.

24. Laboratório guiado

Abra a lista dinâmica.

Etapa 1 — Alterne o filtro

Observe property, attribute e class bindings refletidos na prévia.

Etapa 2 — Conclua tarefas

O event binding chama uma operação por identificador. O item conserva identidade.

Etapa 3 — Esvazie o resultado

Use o filtro e remova itens até aparecer o equivalente ao bloco @empty.

Etapa 4 — Examine a fonte

Alterne entre TypeScript e template. Relacione cada controle visual à sintaxe Angular equivalente.

25. Sobre o laboratório estático

O laboratório abre sem instalar Angular e reproduz o comportamento com JavaScript. A pasta src/app contém a fonte Angular equivalente. O objetivo é isolar a sintaxe de templates antes da instalação do espaço de trabalho completo.

26. Erros comuns

Usar atributo textual para booleano

disabled="false" ainda mantém o atributo. Use [disabled]="condition".

Usar índice como identidade de lista mutável

Inserções e reordenação podem associar view ao item errado.

Colocar regra de negócio no template

Expressões ficam longas, repetidas e difíceis de testar.

Usar $event.target sem narrowing

O target pode não ser o elemento esperado.

Esconder estado vazio

Uma lista sem itens precisa explicar o resultado ao usuário.

Usar innerHTML com entrada externa

Binding de texto é a escolha segura para conteúdo textual.

27. Boas práticas

  • mantenha templates declarativos;
  • use elementos semânticos;
  • escolha a categoria correta de binding;
  • trate eventos em métodos claros;
  • use signals para estado mutável do template;
  • derive filtros fora da marcação;
  • use identificadores estáveis em track;
  • implemente @empty;
  • mantenha foco e mensagens acessíveis;
  • evite HTML dinâmico não confiável.

28. Exercícios

Exercício 1 — Interpolação

Mostre título e quantidade total de tarefas.

Exercício 2 — Propriedade

Desabilite um botão quando a lista estiver vazia.

Exercício 3 — Evento

Implemente toggleTask(id).

Exercício 4 — Control flow

Crie estados loading, error, lista e vazio.

Exercício 5 — Track

Explique por que task.id é melhor que $index para a lista editável.

29. Desafio

Crie uma lista que:

  • filtre por texto e status;
  • mostre quantidade visível;
  • utilize @for com track task.id;
  • possua @empty;
  • alterne conclusão por evento;
  • associe aria-pressed ao filtro;
  • desabilite ações impossíveis;
  • não execute regra extensa no template.

30. Lista de verificação de conclusão

  • Sei o que Angular compila no template.
  • Diferencio texto estático e interpolação.
  • Diferencio property e attribute binding.
  • Uso class e style binding.
  • Respondo a eventos.
  • Faço narrowing de $event.target.
  • Uso @if e @else.
  • Uso @for com identidade estável.
  • Sei quando não usar $index.
  • Represento lista vazia com @empty.

31. Critérios de avaliação

Critério Pontos
Interpolação e expressões 15
Property e attribute bindings 20
Eventos 15
@if e estados 15
@for, track e @empty 25
Acessibilidade e clareza 10
Total 100

Resumo

Nesta aula, aprendemos que:

  • template Angular é HTML compilado com sintaxes adicionais;
  • binding conecta estado e view;
  • propriedade DOM e atributo HTML não são iguais;
  • eventos chamam comportamentos da classe;
  • @if cria views condicionais;
  • @for repete e acompanha itens;
  • track requer identidade estável;
  • @empty representa coleção vazia;
  • lógica complexa deve sair do template.

Próxima aula

Na Aula 4.3, separaremos a lista e os cartões em componentes reutilizáveis e criaremos comunicação explícita com inputs e outputs.

Fontes oficiais