💻 Exemplos: Capítulo 02: Templates, Property Binding, Event Binding e Two-Way Binding
Especialização em Front-End com Angular • Demonstrações Práticas Compiláveis
💻 Código de Exemplo 02 — Data Binding Unidirecional e Bidirecional com Standalone Components
Este exemplo apresenta os 4 pilares de Data Binding do Angular: Interpolação {{ }}, Property Binding [prop], Event Binding (event) com modificadores de teclado e Two-Way Data Binding [(ngModel)] através do FormsModule.
// src/app/components/server-config-editor.component.ts
import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
interface DatabaseServerConfig {
hostname: string;
port: number;
maxConnections: number;
sslEnabled: boolean;
environment: 'staging' | 'production' | 'disaster-recovery';
}
@Component({
selector: 'app-server-config-editor',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<div class="max-w-xl mx-auto p-6 bg-white border border-slate-200 rounded-2xl shadow-sm space-y-6">
<div class="border-b pb-3">
<h2 class="text-xl font-bold text-slate-800">
⚙️ Editor de Configuração do Banco de Dados
</h2>
<p class="text-xs text-slate-500">
Demonstração de Data Binding no Angular 18 Standalone
</p>
</div>
<!-- Formulário de Edição Interativa -->
<div class="space-y-4">
<!-- Two-Way Binding com [(ngModel)] -->
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">
Hostname do Servidor:
</label>
<input
type="text"
[(ngModel)]="config.hostname"
class="w-full border px-3 py-2 rounded-lg text-sm outline-none focus:ring-2 focus:ring-red-500"
/>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Porta TCP:</label>
<input
type="number"
[(ngModel)]="config.port"
class="w-full border px-3 py-2 rounded-lg text-sm"
/>
</div>
<div>
<label class="block text-xs font-semibold text-slate-700 mb-1">Ambiente:</label>
<select
[(ngModel)]="config.environment"
class="w-full border px-3 py-2 rounded-lg text-sm bg-white"
>
<option value="staging">Staging (Homologação)</option>
<option value="production">Production (Produção)</option>
<option value="disaster-recovery">Disaster Recovery (DR)</option>
</select>
</div>
</div>
<!-- Property Binding de Estilo e Atributo -->
<div>
<div class="flex justify-between text-xs font-semibold text-slate-700 mb-1">
<span>Conexões Máximas Permitidas:</span>
<span class="font-mono text-red-600 font-bold">{{ config.maxConnections }}</span>
</div>
<input
type="range"
min="50"
max="1000"
step="50"
[(ngModel)]="config.maxConnections"
class="w-full h-2 bg-slate-200 rounded-lg cursor-pointer"
/>
<!-- Barra de Progresso com Property Binding de Estilo dinâmico [style.width.%] -->
<div class="w-full bg-slate-100 rounded-full h-2 mt-2 overflow-hidden border">
<div
class="h-2 rounded-full transition-all duration-300"
[style.width.%]="(config.maxConnections / 1000) * 100"
[ngClass]="{
'bg-emerald-500': config.maxConnections <= 400,
'bg-amber-500': config.maxConnections > 400 && config.maxConnections <= 750,
'bg-red-600': config.maxConnections > 750
}"
></div>
</div>
</div>
<label class="flex items-center gap-2 text-sm text-slate-700 cursor-pointer pt-1">
<input type="checkbox" [(ngModel)]="config.sslEnabled" class="rounded text-red-600" />
<span>Exigir Conexão Criptografada SSL/TLS Obrigatória</span>
</label>
</div>
<!-- Event Binding com (click) e Property Binding [disabled] -->
<div class="flex gap-3 pt-2 border-t">
<button
type="button"
(click)="saveConfiguration()"
[disabled]="isSaving()"
class="flex-1 py-2.5 bg-red-600 hover:bg-red-700 text-white rounded-xl text-xs font-bold transition-colors disabled:opacity-50"
>
{{ isSaving() ? 'Persistindo no Cluster...' : 'Aplicar Configuração' }}
</button>
<button
type="button"
(click)="resetDefaults()"
class="px-4 py-2.5 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-xl text-xs font-semibold"
>
Resetar
</button>
</div>
<!-- Interpolação e Diagnóstico JSON em Tempo Real -->
<div class="p-4 bg-slate-900 text-slate-200 rounded-xl font-mono text-xs space-y-1">
<div class="text-emerald-400 font-bold">// Estado em Tempo Real (Interpolação):</div>
<div>URI: {{ buildConnectionUri() }}</div>
<div>Segurança: {{ config.sslEnabled ? 'SSL_VERIFIED' : 'PLAINTEXT_INSECURE' }}</div>
</div>
</div>
`
})
export class ServerConfigEditorComponent {
config: DatabaseServerConfig = {
hostname: 'primary-db.prod.internal',
port: 5432,
maxConnections: 500,
sslEnabled: true,
environment: 'production'
};
isSaving = signal<boolean>(false);
buildConnectionUri(): string {
const protocol = this.config.sslEnabled ? 'postgresql+ssl://' : 'postgresql://';
return `${protocol}${this.config.hostname}:${this.config.port}/analytics_db`;
}
saveConfiguration(): void {
this.isSaving.set(true);
setTimeout(() => {
this.isSaving.set(false);
console.log('[Config Saved] Servidor atualizado:', this.config);
}, 800);
}
resetDefaults(): void {
this.config = {
hostname: 'localhost',
port: 5432,
maxConnections: 100,
sslEnabled: false,
environment: 'staging'
};
}
}
🖥️ Saída / Comportamento Esperado
Renderização Inicial:
Hostname: "primary-db.prod.internal" | Porta: 5432 | Ambiente: Production
Conexões: 500 / 1000 (Barra 50% preenchida na cor âmbar)
URI Gerada: "postgresql+ssl://primary-db.prod.internal:5432/analytics_db"
Interação 1 — Alterar Hostname:
Digitar "replica-read-01.corp" no campo:
A interpolação no bloco escuro atualiza instantaneamente para "postgresql+ssl://replica-read-01.corp:5432/analytics_db".
Interação 2 — Deslizar conexões para 850:
A barra de progresso avança para 85% e sua classe muda dinamicamente para 'bg-red-600' através do [ngClass].
Interação 3 — Clicar em "Aplicar Configuração":
O botão exibe "Persistindo no Cluster..." e fica desabilitado via [disabled]="isSaving()".
Após 800ms, o botão volta ao normal e o console registra o objeto atualizado.
🔍 Destaques da Implementação
- Binding de Propriedade de Estilo Direto:
[style.width.%]="..."elimina concatenações de strings"width: " + valor + "%". - Two-Way Binding com
[(ngModel)]: O operador “banana in a box” sincroniza automaticamente a propriedade da classe com o eventoinputdo HTML. - Imunidade a Injeção: A interpolação
{{ }}realiza sanitização automática contra vulnerabilidades de Cross-Site Scripting (XSS).