Capítulo 02: Templates, Property Binding, Event Binding e Two-Way Binding
Capítulo 02: Templates, Property Binding, Event Binding e Two-Way Binding
Especialização em Front-End com Angular • Angular 18+ • Signals, Standalone Architecture, RxJS, NgRx e SSR
🗺️ Mapa Conceitual do Sistema de Data Binding do Angular
flowchart TD
A["Estado na Classe TypeScript (.ts)"] -->|"1. Interpolação: {{ expr }}"| B["DOM / HTML Renderizado"]
A -->|"2. Property Binding: [prop]='valor'"| B
B -->|"3. Event Binding: (evento)='metodo($event)'"| A
A <-->|"4. Two-Way Binding: [(ngModel)]='propriedade'"| B
subgraph SEGURANCA["Segurança e Sanitização Nativa"]
C["DomSanitizer: Prevenção Automática de XSS"]
D["Context-Aware Escaping de URLs e Scripts"]
E["Diferenciação Estrita: [prop] vs [attr.nome]"]
end
B --- SEGURANCA
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style SEGURANCA fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
🏛️ 1. O Compilador de Templates e os Modos de Vinculação de Dados
No Angular, os templates HTML não são meras strings avaliadas em tempo de execução; eles são compilados estaticamente durante o build em instruções otimizadas de criação e atualização de nós do DOM. O sistema de vinculação de dados (Data Binding) conecta o estado da classe do componente à árvore de visualização.
1.1 Os 4 Pilares de Data Binding
- Interpolação (
{{ expressao }}): Avalia expressões síncronas e insere texto puro no nó DOM. Se a expressão retornarnullouundefined, o Angular insere uma string vazia silenciosamente sem quebrar o layout. - Property Binding (
[propriedade]="valor"): Conecta uma propriedade do elemento DOM (ex:disabled,src,hidden,value) ou de uma@Input/input()de componente filho a um dado do componente pai. - Event Binding (
(evento)="handler($event)"): Escuta eventos nativos do navegador (click,keyup,submit) ou saídas customizadas (output()), invocando métodos da classe. - Two-Way Data Binding (
[(ngModel)]="valor"): Conhecido como sintaxe “banana in a box”, combina Property Binding com Event Binding para formulários bidirecionais. Exige a importação explícita deFormsModule.
1.2 Property Binding vs Attribute Binding ([attr.nome])
Uma das maiores fontes de confusão no Angular é a diferença entre propriedades do objeto DOM e atributos HTML:
- Propriedade DOM: Representa o estado vivo em memória do elemento (ex:
HTMLInputElement.disabled). Controla-se com[disabled]="condicao". - Atributo HTML: Representa o texto original declarado no HTML estático (ex:
colspan,aria-label,data-testid). Não são propriedades nativas do objeto JavaScript do elemento, exigindo o prefixoattr.:<button [attr.aria-label]="isExpanded ? 'Recolher menu' : 'Expandir menu'">
1.3 Segurança Contra Ataques XSS e DomSanitizer
O Angular trata todo valor inserido via interpolação ou property binding como não confiável por padrão. Ele aplica automaticamente o Context-Aware Escaping:
- Se uma string contiver
<script>alert('hack')</script>, o Angular sanitiza os caracteres transformando-os em entidades HTML inofensivas. - Para injetar HTML de forma deliberada (ex: rich text de um CMS), deve-se utilizar a propriedade
[innerHTML]em conjunto com o serviçoDomSanitizer(bypassSecurityTrustHtml).
⚖️ 2. Tabela Comparativa de Sintaxes de Data Binding
| Tipo de Binding | Sintaxe Canônica | Direção do Fluxo | Exemplo de Aplicação |
|---|---|---|---|
| Interpolação | {{ expressao }} |
Classe ➔ DOM | <h3>{{ user.name }}</h3> |
| Property Binding | [prop]="valor" |
Classe ➔ DOM | <button [disabled]="isPending"> |
| Attribute Binding | [attr.nome]="valor" |
Classe ➔ DOM | <td [attr.colspan]="colCount"> |
| Class Binding | [class.nome]="bool" |
Classe ➔ DOM | <div [class.is-active]="active"> |
| Style Binding | [style.prop.unidade]="num" |
Classe ➔ DOM | <div [style.width.%]="progresso"> |
| Event Binding | (evento)="acao($event)" |
DOM ➔ Classe | <input (keydown.enter)="salvar()"> |
| Two-Way Binding | [(ngModel)]="prop" |
Classe 🔁 DOM | <input [(ngModel)]="username"> |
⚠️ 3. Armadilhas Comuns em Produção (Gotchas & Anti-Patterns)
Anti-Pattern 1: Executar Funções Pesadas dentro de Interpolações {{ }}
Erro Comum: Escrever <span>{{ calcularRelatorioComplexo(dados) }}</span> no template.
Consequência: Em aplicações com Zone.js, essa função será executada dezenas de vezes a cada clique, scroll ou movimento de mouse, destruindo a taxa de quadros (FPS) da aplicação.
Abordagem Correta: Pré-calcule o valor em uma propriedade, utilize um computed() signal ou crie um Pure Pipe (pure: true).
Anti-Pattern 2: Esquecer de Importar FormsModule ao Usar [(ngModel)]
Erro Comum: Utilizar [(ngModel)] em um componente standalone sem declarar imports: [FormsModule]. O compilador gerará o erro: Can't bind to 'ngModel' since it isn't a known property of 'input'.
💻 4. Código Conceitual Arquitetural Comentado
// src/app/components/server-port-editor.component.ts
import { Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-server-port-editor',
standalone: true,
imports: [FormsModule],
template: `
<div class="max-w-md mx-auto p-5 bg-white border border-slate-200 rounded-2xl shadow-sm space-y-4">
<h3 class="font-bold text-slate-800 text-sm">Configuração de Porta de Rede</h3>
<!-- Two-Way Binding com [(ngModel)] -->
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Porta do Serviço:</label>
<input
type="number"
[(ngModel)]="portNumber"
class="w-full border px-3 py-2 rounded-lg text-sm outline-none focus:ring-2 focus:ring-red-500"
[class.border-red-400]="isPrivilegedPort()"
/>
</div>
<!-- Class Binding e Property Binding de Estilo dinâmico -->
<div
class="p-3 rounded-xl border text-xs flex justify-between items-center transition-colors"
[class]="isPrivilegedPort() ? 'bg-amber-50 border-amber-200 text-amber-800' : 'bg-slate-50 border-slate-200 text-slate-700'"
>
<span>Classificação:</span>
<strong class="font-mono">
{{ isPrivilegedPort() ? 'Porta Privilegiada (Root Obrigatório)' : 'Porta de Usuário Livre' }}
</strong>
</div>
<!-- Event Binding com modificador de tecla enter -->
<button
type="button"
(click)="applyPort()"
[disabled]="portNumber < 1 || portNumber > 65535"
class="w-full py-2 bg-red-600 hover:bg-red-700 text-white rounded-xl text-xs font-bold transition-colors disabled:opacity-40"
>
Validar e Salvar Porta
</button>
</div>
`
})
export class ServerPortEditorComponent {
portNumber = 8080;
isPrivilegedPort(): boolean {
return this.portNumber < 1024;
}
applyPort(): void {
console.log(`[Config] Porta ${this.portNumber} validada e salva no cluster.`);
}
}
🛠️ 5. Cheat-Sheet de Data Binding
<!-- Property e Attribute Bindings -->
<img [src]="avatarUrl" [alt]="userName" />
<button [attr.aria-disabled]="isDisabled">Acessibilidade</button>
<!-- Style e Class Bindings -->
<div [class.is-danger]="hasErrors" [class.is-valid]="isValid"></div>
<div [style.max-height.px]="maxHeight" [style.color]="statusColor"></div>
<!-- Event Bindings com Modificadores -->
<input (keydown.enter)="submeter()" (keydown.esc)="cancelar()" />
<div (click)="onClick($event)"></div>