Pular para conteúdo

Aula 19 - Formulários Reativos Avançados e Validadores Customizados 📝

Objetivo Pedagógico

Objetivo: Construção de formulários complexos orientados a modelo com FormGroup, FormArray, tipagem estrita e validadores síncronos e assíncronos customizados.


📑 1. Fundamentos Teóricos & Análise Técnica

O módulo Reactive Forms do Angular adota uma abordagem estritamente funcional e orientada a dados para o gerenciamento de formulários. O estado e a validação do formulário são definidos de forma explícita na classe TypeScript através de classes como FormControl, FormGroup e FormArray, enquanto o template apenas se vincula declarativamente aos controles.

A partir do Angular 14+, os formulários reativos são estritamente tipados (Strictly Typed Forms), garantindo que o compilador verifique o tipo dos valores de cada campo em tempo de compilação, prevenindo erros em tempo de execução ao acessar propriedades inexistentes.

A validação de regras de negócio avançadas é realizada através de: 1. Validadores Síncronos: Funções puras que recebem um controle e retornam um mapa de erros (ValidationErrors | null) de forma síncrona. 2. Validadores Assíncronos: Funções que retornam uma Promise ou Observable emitindo o resultado após consultar um backend (ex: verificação de duplicidade de e-mail ou validação de CPF/CNPJ em serviço remoto). 3. FormArray Dinâmico: Permite adição e remoção programática de campos de entrada em tempo de execução (ex: lista dinâmica de telefones ou itens de fatura).

📐 Arquitetura Conceitual & Diagrama de Fluxo

graph LR
    Model["FormGroup Tipado (TypeScript)"] --> Validators["Validadores (Sync / Async)"]
    Model <--> Template["Template HTML ([formGroup])"]
    Validators --> ValidationState["Estado (valid, invalid, dirty, touched)"]
    ValidationState --> UI["Feedback Visual de Validação"]
    style Model fill:#e3f2fd,stroke:#1565c0
    style Validators fill:#fff3e0,stroke:#e65100
    style UI fill:#e8f5e9,stroke:#2e7d32

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Formulários Estritamente Tipados: Garantia de tipos em tempo de compilação para form.value e métodos como patchValue. - Validadores Assíncronos Não-Bloqueantes: Consultas assíncronas que mantêm o status PENDING enquanto aguardam resposta de APIs. - Manipulação Dinâmica com FormArray: Coleções reativas de controles de formulário que crescem conforme a demanda do usuário. - Imutabilidade e Observabilidade: Acesso aos streams reativos de mudanças via valueChanges e statusChanges.


🛠️ 2. Implementação Prática em Angular Reactive Forms

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// user-form.component.ts (FormGroup Tipado com Validador Customizado)
import { Component, inject } from '@angular/core';
import { FormBuilder, Validators, ReactiveFormsModule, AbstractControl, ValidationErrors } from '@angular/forms';
import { CommonModule } from '@angular/common';

// Validador Customizado: Exige e-mail corporativo específico
export function corporateEmailValidator(control: AbstractControl): ValidationErrors | null {
  const value = control.value as string;
  if (!value) return null;
  return value.endsWith('@empresa.com') ? null : { nonCorporateEmail: true };
}

@Component({
  selector: 'app-user-form',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  template: `
    <form [formGroup]="userForm" (ngSubmit)="onSubmit()">
      <input formControlName="name" placeholder="Nome Completo">
      <input formControlName="email" placeholder="E-mail Corporativo">
      <span *ngIf="userForm.controls.email.errors?.['nonCorporateEmail']" class="error">
        Deve ser um e-mail @empresa.com!
      </span>
      <button type="submit" [disabled]="userForm.invalid">Cadastrar</button>
    </form>
  `
})
export class UserFormComponent {
  private readonly fb = inject(FormBuilder);

  readonly userForm = this.fb.group({
    name: ['', [Validators.required, Validators.minLength(3)]],
    email: ['', [Validators.required, Validators.email, corporateEmailValidator]]
  });

  onSubmit(): void {
    if (this.userForm.valid) {
      console.log('Dados submetidos com sucesso:', this.userForm.value);
    }
  }
}

💡 Análise Passo a Passo do Código

  1. Validador Customizado: corporateEmailValidator inspeciona o valor e retorna um objeto de erro caso o domínio não seja aceito.
  2. FormGroup Tipado: fb.group() infere os tipos dos campos como strings não-nulas de acordo com os valores iniciais.
  3. Feedback Declarativo: O template avalia userForm.controls.email.errors?.['nonCorporateEmail'] para exibir a mensagem corretiva.

🎯 3. Próximos Passos & Sequência Didática