Especialização em Front-End com Angular • Demonstrações Práticas Compiláveis


💻 Código de Exemplo 04 — Transformação Declarativa com Pipes Nativos e Custom Pure Pipe

Este exemplo apresenta a formatação de dados com pipes nativos do Angular (CurrencyPipe, DatePipe, DecimalPipe, PercentPipe) e a criação de um Custom Pure Pipe standalone para conversão de bytes em grandezas legíveis (KB, MB, GB, TB).

1. Criação do Custom Pipe Standalone: src/app/pipes/file-size.pipe.ts

// src/app/pipes/file-size.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'fileSize',
  standalone: true,
  pure: true // Pure Pipes só re-executam quando a referência de entrada (bytes) mudar!
})
export class FileSizePipe implements PipeTransform {
  transform(bytes: number, precision: number = 2): string {
    if (isNaN(bytes) || bytes < 0) return '0 B';
    if (bytes === 0) return '0 B';

    const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    const k = 1024;
    const i = Math.floor(Math.log(bytes) / Math.log(k));

    const converted = parseFloat((bytes / Math.pow(k, i)).toFixed(precision));
    return `${converted} ${units[i]}`;
  }
}

2. Componente Consumidor: src/app/components/backup-audit.component.ts

// src/app/components/backup-audit.component.ts
import { Component, signal } from '@angular/core';
import { CommonModule, CurrencyPipe, DatePipe, DecimalPipe, PercentPipe } from '@angular/common';
import { FileSizePipe } from '../pipes/file-size.pipe';

interface BackupSnapshot {
  id: string;
  database: string;
  sizeBytes: number;
  costPerHour: number;
  compressionRatio: number;
  createdAt: string;
}

@Component({
  selector: 'app-backup-audit',
  standalone: true,
  imports: [CommonModule, FileSizePipe, CurrencyPipe, DatePipe, DecimalPipe, PercentPipe],
  template: `
    <div class="max-w-3xl mx-auto p-6 bg-white border border-slate-200 rounded-2xl shadow-sm space-y-6">
      <div class="flex justify-between items-center border-b pb-4">
        <div>
          <h2 class="text-xl font-bold text-slate-800">💾 Relatório de Armazenamento e Backups</h2>
          <p class="text-xs text-slate-500">Transformação com Pipes Nativos e Custom FileSizePipe</p>
        </div>
        <button
          type="button"
          (click)="addSimulatedBackup()"
          class="px-3.5 py-2 text-xs font-semibold bg-red-600 hover:bg-red-700 text-white rounded-xl transition-colors"
        >
          + Gerar Snapshot
        </button>
      </div>

      <!-- Grade de Registros Formatados via Pipes -->
      <div class="border rounded-xl overflow-hidden">
        <table class="w-full text-left text-xs">
          <thead class="bg-slate-50 text-slate-600 font-semibold border-b">
            <tr>
              <th class="px-4 py-3">Banco Alvo</th>
              <th class="px-4 py-3">Tamanho Real (Custom Pipe)</th>
              <th class="px-4 py-3 text-right">Custo Estimado</th>
              <th class="px-4 py-3 text-center">Taxa de Compressão</th>
              <th class="px-4 py-3 text-right">Data/Hora (DatePipe)</th>
            </tr>
          </thead>
          <tbody class="divide-y text-slate-700">
            @for (item of snapshots(); track item.id) {
              <tr class="hover:bg-slate-50 transition-colors">
                <td class="px-4 py-3 font-bold text-slate-800">{{ item.database }}</td>

                <!-- Custom Pipe fileSize com parâmetro de precisão 1 casa decimal -->
                <td class="px-4 py-3 font-mono font-bold text-indigo-600">
                  {{ item.sizeBytes | fileSize:1 }}
                </td>

                <!-- CurrencyPipe configurado para Real Brasileiro (BRL) -->
                <td class="px-4 py-3 font-mono text-right font-medium">
                  {{ item.costPerHour | currency:'BRL':'symbol':'1.2-2' }}
                </td>

                <!-- PercentPipe para percentual formatado -->
                <td class="px-4 py-3 text-center">
                  <span class="px-2 py-0.5 rounded-full font-bold text-[10px] bg-emerald-100 text-emerald-800">
                    {{ item.compressionRatio | percent:'1.1-1' }}
                  </span>
                </td>

                <!-- DatePipe formatando ISO string para padrão dd/MM/yyyy HH:mm -->
                <td class="px-4 py-3 text-right text-slate-500 font-mono">
                  {{ item.createdAt | date:'dd/MM/yyyy HH:mm:ss' }}
                </td>
              </tr>
            }
          </tbody>
        </table>
      </div>
    </div>
  `
})
export class BackupAuditComponent {
  snapshots = signal<BackupSnapshot[]>([
    {
      id: 'snap-01',
      database: 'PostgreSQL Prod Principal',
      sizeBytes: 15428901234, // ~14.37 GB
      costPerHour: 4.85,
      compressionRatio: 0.684,
      createdAt: '2026-09-28T09:15:30Z'
    },
    {
      id: 'snap-02',
      database: 'MongoDB Document Cluster',
      sizeBytes: 854012900, // ~814.4 MB
      costPerHour: 1.25,
      compressionRatio: 0.452,
      createdAt: '2026-09-28T10:00:00Z'
    },
    {
      id: 'snap-03',
      database: 'Redis Session Cache Dump',
      sizeBytes: 42100500, // ~40.1 MB
      costPerHour: 0.35,
      compressionRatio: 0.825,
      createdAt: '2026-09-28T10:45:10Z'
    }
  ]);

  addSimulatedBackup(): void {
    const newSnap: BackupSnapshot = {
      id: `snap-${Date.now()}`,
      database: `Replica Analytics #${this.snapshots().length + 1}`,
      sizeBytes: Math.floor(Math.random() * 5000000000) + 100000000,
      costPerHour: Number((Math.random() * 3 + 0.5).toFixed(2)),
      compressionRatio: Number((Math.random() * 0.4 + 0.5).toFixed(3)),
      createdAt: new Date().toISOString()
    };
    this.snapshots.update((curr) => [newSnap, ...curr]);
  }
}

🖥️ Saída / Comportamento Esperado

Tabela de Backups Formatada via Pipes:
Linha 1:
PostgreSQL Prod Principal | 14.4 GB | R$ 4,85 | 68.4% | 28/09/2026 09:15:30
Linha 2:
MongoDB Document Cluster  | 814.4 MB | R$ 1,25 | 45.2% | 28/09/2026 10:00:00
Linha 3:
Redis Session Cache Dump  | 40.1 MB  | R$ 0,35 | 82.5% | 28/09/2026 10:45:10

Ao clicar no botão "+ Gerar Snapshot":
Um novo objeto com timestamp UTC e bytes brutos (ex: 2840192300) é inserido.
O FileSizePipe converte instantaneamente para "2.6 GB".
O CurrencyPipe formata o valor monetário com símbolo "R$".
O DatePipe localiza a data/hora para o fuso brasileiro sem mutar o dado original.

🔍 Destaques da Implementação

  1. pure: true (Memoização Padrão): O Angular só recalcula a função transform se o valor primitivo ou a referência do objeto mudar, evitando execuções desnecessárias a cada ciclo de renderização.
  2. Encadeamento de Pipes (Pipe Chaining): Permite encadear transformações (ex: {{ valor | pipeA | pipeB }}).
  3. Internacionalização Conforme: CurrencyPipe respeita as regras monetárias do código ISO (BRL) e formatação de decimais 1.2-2.