Identificação
- Duração: 2 horas
- Tipo: teoria aplicada e laboratório
- Entrega: lista dinâmica de tarefas
- Laboratório:
../exemplos/aula-4.2/index.html
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:
- explicar o que é template Angular;
- diferenciar HTML estático e binding;
- utilizar interpolação;
- criar property bindings;
- criar attribute bindings;
- aplicar class e style bindings;
- responder a eventos;
- utilizar
$eventcom cuidado; - criar condições com
@if; - repetir itens com
@for; - escolher uma expressão
trackestável; - 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
@forcomtrack task.id; - possua
@empty; - alterne conclusão por evento;
- associe
aria-pressedao 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
@ife@else. - Uso
@forcom 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;
@ifcria views condicionais;@forrepete e acompanha itens;trackrequer identidade estável;@emptyrepresenta 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.