Capítulo 04: Pipes Nativos (DatePipe, CurrencyPipe) e Custom Pipes
Capítulo 04: Pipes Nativos (DatePipe, CurrencyPipe) e Custom Pipes
Especialização em Front-End com Angular • Angular 18+ • Signals, Standalone Architecture, RxJS, NgRx e SSR
🗺️ Mapa Conceitual de Pipes e Transformação de Dados na View
flowchart TD
A["Estado Bruto do Componente (Model / Signal)"] --> B["Template Expression (HTML)"]
B --> C["Operador Pipe (|)"]
subgraph PIPES_ENGINE["Mecanismo de Transformação de Pipes"]
D["Pipes Puros (Pure: true, Default)"]
E["Pipes Impuros (Pure: false)"]
F["Pipes Nativos (@angular/common)"]
G["Custom Pipes (@Pipe Standalone)"]
end
C --> PIPES_ENGINE
D --> H["Memoização por Referência de Entrada (Object.is)"]
E --> I["Execução em Todo Ciclo de Change Detection"]
F --> J["DatePipe, CurrencyPipe, DecimalPipe, JsonPipe, AsyncPipe"]
G --> K["Implementação de PipeTransform.transform()"]
H & J & K --> L["Renderização Formatada no DOM da View"]
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style PIPES_ENGINE fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style L fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
🏛️ 1. Papel Arquitetural dos Pipes no Angular 18+
No ecossistema Angular, os Pipes são funções puras de formatação declarativa que transformam valores brutos (números, datas, strings, objetos) em formatos amigáveis para exibição visual no template HTML, sem mutar o estado original armazenado nos signals ou no componente.
1.1 Separação Rígida entre Lógica de Negócio e Apresentação
Um dos maiores erros em arquiteturas de front-end é poluir o TypeScript com métodos de formatação invocados dentro de templates ({{ formatarData(item.data) }}). Métodos invocados em templates são reexecutados a cada ciclo de detecção de mudanças (Change Detection), degradando drasticamente o FPS da aplicação.
Os Pipes resolvem esse gargalo através de memoização nativa:
- O Angular armazena em cache o último valor de entrada e o último resultado calculado.
- Se a referência da entrada não mudou (
Object.is(oldVal, newVal) === true), o pipe simplesmente retorna o valor cacheado sem reexecutar seu código.
⚙️ 2. Pipes Nativos Essenciais do Angular Standalone
Com a arquitetura Standalone, os pipes nativos do pacote @angular/common devem ser importados diretamente no array imports do componente:
import { Component, signal } from '@angular/core';
import { DatePipe, CurrencyPipe, DecimalPipe, PercentPipe, UpperCasePipe, JsonPipe } from '@angular/common';
@Component({
selector: 'app-relatorio-vendas',
standalone: true,
imports: [DatePipe, CurrencyPipe, DecimalPipe, PercentPipe, UpperCasePipe, JsonPipe],
template: `
<div class="card">
<h2>Relatório: {{ empresa() | uppercase }}</h2>
<p>Data de Emissão: {{ dataEmissao() | date:'dd/MM/yyyy HH:mm' }}</p>
<p>Total Faturado: {{ faturamento() | currency:'BRL':'symbol':'1.2-2':'pt-BR' }}</p>
<p>Margem de Lucro: {{ margem() | percent:'1.1-2' }}</p>
<p>Volume de Transações: {{ transacoes() | number:'1.0-0' }}</p>
</div>
`
})
export class RelatorioVendasComponent {
empresa = signal('Tech Corp Enterprise');
dataEmissao = signal(new Date());
faturamento = signal(1254390.75);
margem = signal(0.245);
transacoes = signal(8950);
}
2.1 Configuração Global de Locale (pt-BR)
Por padrão, o Angular adota o locale americano (en-US). Para que o CurrencyPipe e o DatePipe utilizem o padrão brasileiro com separador de vírgula e formato monetário em reais, é mandatório registrar os dados de cultura no app.config.ts:
// app.config.ts
import { ApplicationConfig, LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import localePt from '@angular/common/locales/pt';
// Registro oficial do locale no runtime
registerLocaleData(localePt);
export const appConfig: ApplicationConfig = {
providers: [
{ provide: LOCALE_ID, useValue: 'pt-BR' }
]
};
🛠️ 3. Construção de Custom Pipes Standalone
Para transformações de negócio específicas (como formatação de CPF/CNPJ, máscara de cartão, conversão de bytes ou cálculo de tempo decorrido relativo), cria-se um Custom Pipe implementando a interface PipeTransform:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'cpfMask',
standalone: true,
pure: true // Memoizado: reexecuta apenas se o valor de entrada mudar
})
export class CpfMaskPipe implements PipeTransform {
transform(valor: string | number | null | undefined): string {
if (!valor) return '';
const digits = String(valor).replace(/\D/g, '');
if (digits.length !== 11) return digits; // Retorna original se inválido
return digits.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3-$4');
}
}
3.1 Custom Pipe Parametrizado: Formatação de Tamanho de Arquivos (Bytes)
@Pipe({
name: 'fileSize',
standalone: true,
pure: true
})
export class FileSizePipe implements PipeTransform {
transform(bytes: number | null | undefined, decimais: number = 2): string {
if (bytes == null || isNaN(bytes) || bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
const valor = parseFloat((bytes / Math.pow(k, i)).toFixed(decimais));
return `${valor} ${sizes[i]}`;
}
}
Uso no template:
<span>{{ 10485760 | fileSize:1 }}</span> <!-- Renderiza: 10.0 MB -->
⚖️ 4. Comparativo de Design: Pure Pipes vs Impure Pipes
| Dimensão Técnica | Pure Pipe (pure: true) |
Impure Pipe (pure: false) |
|---|---|---|
| Comportamento Padrão | Sim (@Pipe({ pure: true })) |
Não, requer configuração explícita |
| Gatilho de Reavaliação | Apenas quando os argumentos primitivos ou a referência do objeto mudam | A cada ciclo de Change Detection (a cada clique, timer ou requisição) |
| Impacto de Desempenho | Levíssimo (Memoização instantânea em O(1)) | Crítico se mal utilizado (pode travar a thread de UI) |
| Casos de Uso Válidos | 99% das formatações: moedas, datas, máscaras, slugs | Streams assíncronas internas, inspeção de mutações profundas em coleções |
| Exemplo Nativo | DatePipe, CurrencyPipe, DecimalPipe |
AsyncPipe, JsonPipe (quando inspeciona objetos mutados internamente) |
⚠️ 5. Armadilhas Comuns e Anti-Patterns
- Criar Pipes Impuros para Filtrar Listas:
- Anti-Pattern: Criar um
filterPipeimpuro para filtrar um array de 1.000 itens dentro do template com*ngForou@for. O pipe roda centenas de vezes a cada microssegundo. - Correção Padrão Ouro: Realizar filtros no TypeScript utilizando
computed()do Angular Signals. O signal é calculado apenas quando o termo de busca ou a lista mudam.
- Anti-Pattern: Criar um
- Encadear Múltiplos Pipes Pesados sem Critério:
- Risco:
{{ dado | pipeComplexoA | pipeComplexoB | pipeComplexoC }}. - Correção: Se a pipeline de transformação for custosa, centralize a lógica no modelo de dados ou pré-processe no Store/Signal.
- Risco:
- Esquecer de Adicionar o Pipe aos
importsdo Componente Standalone:- Erro: O Angular emitirá o erro
NG8004: No pipe found with name 'cpfMask'. - Correção: Declarar a classe do pipe no array
imports: [CpfMaskPipe]do@Component.
- Erro: O Angular emitirá o erro
🛡️ 6. Boas Práticas e Segurança Corporativa
- Sanitização de HTML: Nunca utilize um pipe para concatenar strings HTML não sanitizadas direto no
[innerHTML]sem passar peloDomSanitizerdo Angular, evitando ataques XSS. - Tipagem Estrita em
transform(): Tipar explicitamente o primeiro argumento e todos os parâmetros opcionais com tipos do TypeScript, tratando sempre entradasnulleundefined. - Testabilidade Desacoplada: Pipes são classes TypeScript comuns com o método
transform(). Eles podem ser testados unitariamente de forma instantânea sem precisar deTestBed.
📋 7. Cheat-Sheet: Resumo Rápido
// Declaração de Pipe Standalone
@Pipe({ name: 'meuPipe', standalone: true, pure: true })
export class MeuPipe implements PipeTransform {
transform(val: string, arg1: number = 0): string {
return val.substring(0, arg1);
}
}
// Template
// {{ texto | meuPipe:10 }}
// Encadeamento:
// {{ valor | currency:'BRL' | lowercase }}