Publicado em

- 5 minutos de leitura

Angular: Quando usar FormGroup, FormRecord e FormArray?

img of Angular: Quando usar FormGroup, FormRecord e FormArray?

O Reactive Forms é uma abordagem poderosa para lidar com formulários no Angular, oferecendo um gerenciamento de estado explícito e imutável.

Ao contrário dos formulários baseados em template (Template Driven Forms), onde a lógica reside no HTML, o Reactive Forms centraliza o estado no código TypeScript.

Isso permite criar fluxos de dados complexos, validações síncronas e assíncronas, e testes unitários muito mais precisos.

Por que usar isso em vez de Template Driven ou Signals Forms

Com a evolução contínua do framework, a tipagem estrita (Strictly Typed Forms) consolidou-se como o padrão.

Isso significa que o compilador do TypeScript consegue entender exatamente a estrutura do seu formulário, prevenindo erros comuns durante o desenvolvimento.

O Template Driven Forms é excelente para formulários simples, e o Signal Forms está surgindo como uma alternativa promissora baseada em reatividade fina.

No entanto, o Reactive Forms continua sendo a escolha principal para aplicações corporativas no Angular 21+.

A principal razão é a sua robustez e a vasta gama de ferramentas maduras já integradas, entregando a segurança e a escalabilidade que grandes projetos exigem atualmente.

O que é FormGroup

O FormGroup é a estrutura fundamental para agrupar campos relacionados.

Ele agrega os valores de cada filho em um único objeto.

Se você tem um formulário de cadastro com “Nome” e “Email”, o FormGroup é o contêiner que segura esses dois campos.

Ele rastreia o estado de validação e o valor de todos os seus filhos.

Se um dos campos estiver inválido, o FormGroup inteiro também será considerado inválido. Veja como agora utilizamos as chaves de erro específicas (required, email) para mostrar mensagens de validação detalhadas:

   import { Component, ChangeDetectionStrategy } from '@angular/core'
import { ReactiveFormsModule, FormGroup, FormControl, Validators } from '@angular/forms'

@Component({
	selector: 'app-user-profile',
	imports: [ReactiveFormsModule],
	template: `
		<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
			<label>
				Nome:
				<input formControlName="name" />
			</label>

			@if (profileForm.controls.name.hasError('required') && profileForm.controls.name.touched) {
				<span class="error">O nome é obrigatório.</span>
			}

			<label>
				Email:
				<input formControlName="email" />
			</label>

			@if (profileForm.controls.email.touched) {
				@if (profileForm.controls.email.hasError('required')) {
					<span class="error">O e-mail é obrigatório.</span>
				}

				@if (profileForm.controls.email.hasError('email')) {
					<span class="error">Digite um formato de e-mail válido.</span>
				}
			}

			<button type="submit" [disabled]="profileForm.invalid">Salvar</button>
		</form>
	`,
	changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserProfileComponent {
	profileForm = new FormGroup({
		name: new FormControl<string>('', {
			nonNullable: true,
			validators: [Validators.required]
		}),
		email: new FormControl<string>('', {
			nonNullable: true,
			validators: [Validators.required, Validators.email]
		})
	})

	onSubmit() {
		console.log(this.profileForm.getRawValue())
	}
}

O que é FormRecord

O FormRecord é uma estrutura especializada introduzida para lidar com coleções de campos onde as chaves não são conhecidas antecipadamente.

Ele funciona de forma semelhante a um FormGroup, mas com a restrição de que todos os valores dentro dele devem compartilhar o mesmo tipo.

Imagine um sistema onde você precisa definir configurações que vêm do servidor, como preferências de notificação.

Você não sabe exatamente quais chaves virão da API, mas sabe que todas serão booleanas (verdadeiro ou falso).

O FormRecord permite adicionar e remover esses campos dinamicamente mantendo a segurança de tipo.

   import { Component, ChangeDetectionStrategy, signal } from '@angular/core'
import { ReactiveFormsModule, FormRecord, FormControl } from '@angular/forms'

@Component({
	selector: 'app-dynamic-settings',
	imports: [ReactiveFormsModule],
	template: `
		<form [formGroup]="settingsForm" (ngSubmit)="saveSettings()">
			<h3>Configurações de Notificação</h3>

			@for (setting of settingKeys(); track setting) {
				<div>
					<label>
						<input type="checkbox" [formControlName]="setting" />
						{{ setting }}
					</label>
				</div>
			}

			<button type="submit" [disabled]="settingsForm.invalid">Salvar Configurações</button>
		</form>
	`,
	changeDetection: ChangeDetectionStrategy.OnPush
})
export class DynamicSettingsComponent {
	settingsForm = new FormRecord<FormControl<boolean>>({})

	settingKeys = signal<string[]>([])

	constructor() {
		this.loadSettingsFromApi()
	}

	loadSettingsFromApi() {
		const apiResponse: Record<string, boolean> = {
			emailNotifications: true,
			smsNotifications: false,
			pushNotifications: true
		}

		const keys = Object.keys(apiResponse)
		this.settingKeys.set(keys)

		keys.forEach((key) => {
			this.settingsForm.addControl(
				key,
				new FormControl<boolean>(apiResponse[key], { nonNullable: true })
			)
		})
	}

	saveSettings() {
		console.log(this.settingsForm.getRawValue())
	}
}

O que é FormArray

O FormArray é utilizado para gerenciar uma lista indexada de campos.

Diferente do FormGroup, que usa chaves nomeadas, o FormArray organiza os campos por índices numéricos.

Ele é perfeito para cenários onde a quantidade de itens é variável e decidida pelo usuário.

Um bom exemplo é quando o usuário precisa clicar em um botão para adicionar múltiplos números de telefone.

   import { Component, ChangeDetectionStrategy } from '@angular/core'
import { ReactiveFormsModule, FormGroup, FormArray, FormControl, Validators } from '@angular/forms'

@Component({
	selector: 'app-skills-list',
	imports: [ReactiveFormsModule],
	template: `
		<form [formGroup]="form">
			<h3>Minhas Habilidades</h3>

			<div formArrayName="skills">
				@for (skillField of skillsArray.controls; track $index) {
					<div class="skill-row">
						<input [formControlName]="$index" placeholder="Ex: Angular" />
						<button type="button" (click)="removeSkill($index)">Remover</button>
					</div>
				}
			</div>

			<button type="button" (click)="addSkill()">Adicionar Habilidade</button>
		</form>
	`,
	changeDetection: ChangeDetectionStrategy.OnPush
})
export class SkillsListComponent {
	form = new FormGroup({
		skills: new FormArray<FormControl<string>>([])
	})

	get skillsArray() {
		return this.form.controls.skills
	}

	addSkill() {
		const newSkillField = new FormControl<string>('', {
			nonNullable: true,
			validators: [Validators.required]
		})

		this.skillsArray.push(newSkillField)
	}

	removeSkill(index: number) {
		this.skillsArray.removeAt(index)
	}
}

Imagem com o texto: Curso Formulários com Angular e com a logo do Angular em cor roxa. Logo abaixo existe um botão com o texto "Eu quero!"

Quando usar FormGroup

Use o FormGroup quando você conhece a estrutura do seu formulário em tempo de desenvolvimento.

É o caso de uso mais comum para formulários estáticos, como login ou cadastro de produtos.

Se o objeto que vai enviar para a API tem chaves fixas, o FormGroup é a escolha certa.

Quando usar FormRecord

Use o FormRecord quando você precisa de um grupo de campos aberto, com chaves dinâmicas.

É a ferramenta ideal quando os dados vêm de uma API e você não conhece os nomes das propriedades com antecedência.

A única regra é que todos os valores inseridos compartilhem a mesma tipagem (por exemplo, todos os campos são booleanos ou todos são strings).

Quando usar FormArray

Use o FormArray quando a ordem dos elementos importa ou quando você está lidando com uma lista iterável de itens.

Se você precisa de botões de “Adicionar” e “Remover” blocos de campos idênticos, o FormArray é indispensável.

Ele é ideal para carrinhos de compras, listas de tarefas ou upload de múltiplos arquivos.

Conclusão

Dominar a distinção entre FormGroup, FormRecord e FormArray é essencial para construir formulários escaláveis e seguros no Angular.

A escolha correta da estrutura não só facilita a implementação da lógica de validação, como também garante que a tipagem rigorosa do TypeScript trabalhe a seu favor, evitando bugs silenciosos em tempo de execução.

Ao instanciar suas classes explicitamente, validar corretamente os erros com hasError e adotar as práticas modernas, você ganha uma base de código muito mais previsível e fácil de manter.

Com essas ferramentas bem compreendidas, você estará perfeitamente equipado para resolver qualquer desafio de manipulação de dados nas aplicações complexas do Angular 21+.

Assine nossa Newsletter

Receba novos posts como esse na sua caixa de e-mail!

Sobre o autor

Author's photo
Henrique Custódia Arquiteto Frontend, entusiasta Angular, cat lover, criador de conteúdo e fundador da Code Dimension!