DEV Community

Bruno Donatelli
Bruno Donatelli

Posted on • Edited on

Desenvolvendo Formulários Reativos e Acessíveis no Angular: Um Guia Passo a Passo

Glossário

Observações:

A versão utilizada do Angular, foi a versão 17.2

Para versões versões anteriores, terá o código condizente no repositório deste artigo

Introdução

O que é um formulário HTML?

Formulários são um dos pontos de interação entre uma pessoa e uma aplicação web. No geral, formulários são a principal interface para a coleta de dados, login, inscrições e interações personalizadas.

Um formulário pode possuir campos de texto (linha única ou várias linhas), caixas de seleção, botões, checkboxes ou radio buttons. A maior parte do tempo, esses elementos são combinados com uma legenda que descreve seu objetivo.

Relevância da acessibilidade dentro de um formulário

Quando tornamos os formulários acessíveis, promovemos a igualdade de acesso à informação e serviços online para pessoas com diferentes habilidades e necessidades. Isso é especialmente importante considerando a diversidade de pessoas que utilizam a web, incluindo as com deficiências visuais, auditivas, motoras ou cognitivas.

Ao priorizarmos a acessibilidade na criação de formulários, não estamos só cumprindo as diretrizes e regulamentações internacionais, como as diretrizes da W3C e WCAG, mas também estamos promovendo uma experiência mais inclusiva e acessível para todas as pessoas na web.

Formulários tradicionais

Como os formulários são tradicionalmente criados em aplicações web.

Os formulários geralmente são construídos com tags, como:

form

input

textarea

button

Em suma:

  • o form é usado para agrupar os campos de entrada (input, textarea), e definir o método de envio de dados.
  • input e textarea são utilizados para coletar a informação ou as informações digitadas por uma pessoa.

Além disso, as pessoas desenvolvedoras podem adicionar atributos como placeholder, para exibir um texto de exemplo, quando o input ou textarea está vazio, mostrando à pessoa usuária da aplicação como ela deve inserir uma informação dentro desse campo. Ou seja: fornecer orientações visuais.

<input type="text" placeholder="algum exemplo aqui" />
Enter fullscreen mode Exit fullscreen mode

O envio do formulário geralmente é tratado por meio de um botão com o tipo submit:

<button type="submit">Enviar formulário</button>
Enter fullscreen mode Exit fullscreen mode

que, quando clicado, submete os dados para processamento pelo servidor.

Exemplo de código HTML para um formulário simples.

Vamos começar nosso exemplo com um formulário básico, que contém quatro campos de entrada e um botão de enviar os dados.

<form>
  <div>
    <input type="text" placeholder="informe seu nome" />
  </div>
  <div>
    <input type="text" placeholder="informe seu email" />
  </div>
  <div>
    <input type="text" placeholder="informe o motivo do contato" />
  </div>
  <div>
    <textarea
      name="mensagem"
      id="mensagem"
      cols="30"
      rows="5"
      placeholder="digite sua mensagem"
    ></textarea>
  </div>

  <button type="submit">Nos envie uma mensagem</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Analisando esse formulário, vemos que os inputs e textarea possuem placeholder, e o botão é do tipo submit, o qual vai fazer o envio dos dados desse formulário.
Tendo esse código HTML pronto, vamos integrá-lo em um componente Angular, utilizando Formulários reativos

Inicializando um projeto minimalista em Angular

Tenha instalado em sua máquina, o NodeJS ou bun (Apenas versões a partir do 17 do Angular suportam o bun)

  • Instalando o angular cli:
$ npm i -g @angular/cli@latest
Enter fullscreen mode Exit fullscreen mode
  • Criando o projeto minimalista
$ ng new <NOME_DO_PROJETO> --minimal
Enter fullscreen mode Exit fullscreen mode

Tendo isso, podemos começar a criar nosso formulário reativo!

Formulários Reativos em Angular

Formulários reativos oferecem uma abordagem orientada por modelos, lidando com valores que mudam com o tempo.
O modelo é criado na classe do componente, e utlizado via bind e propriedades dentro dos templates.

Necessário importar o FormsModule e o ReactiveFormsModule dentro do seu component.ts ou em seu módulo

Criação de um formulário reativo no Angular

Para criarmos um formulário reativo, precisamos utilizar as classes FormGroup e FormControl, ou então, utilizarmos o FormBuilder para as instâncias de criação de grupo e controles do formulário. Nesse primeiro exemplo, usaremos FormGroup e FormControl em forma de instância de classe, mas o formulário que criamos acima, vai ser feito utilizando FormBuilder.

exemplo retirado do guia de formulários reativos do site do Angular
Guia aqui

form.component.ts

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  standalone: true,
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css'],
})
export class ProfileEditorComponent {
  profileForm = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
  });
}
Enter fullscreen mode Exit fullscreen mode

form.component.html

<form [formGroup]="profileForm">

  <label for="first-name">First Name: </label>
  <input id="first-name" type="text" formControlName="firstName">

  <label for="last-name">Last Name: </label>
  <input id="last-name" type="text" formControlName="lastName">

</form>
Enter fullscreen mode Exit fullscreen mode

Explicação: O atributo profileForm recebe uma instância da class FormGroup, e para cada propriedade do objeto sendo criado dentro desse grupo, é passado uma instância de FormControl, com inicialização de um string vazio.

No template HTML, adicionamos o atributo formGroup na tag form, com property-bind, adicionando a propriedade do nosso component.ts, profileForm, para relacionarmos o formulário do template HTML, com o FormGroup criado dentro do componente.

Implementando nosso formulário, em um componente Angular

app.component.html

<form [formGroup]="contatoForm" (ngSubmit)="submitForm()">
  <div>
    <input type="text" formControlName="nome" />
  </div>
  <div>
    <label for=""></label>
    <input
      type="text"
      formControlName="email"
      required
      placeholder="exemplo@mail.com"
    />
  </div>
  <div>
    <input type="text" formControlName="assunto" />
  </div>
  <div>
    <textarea
      rows="5"
      cols="21"
      formControlName="mensagem"
      #textarea
      maxlength="300"
    >
    </textarea>
  </div>
  <button
    type="button"
  >
    Nos envie uma mensagem
  </button>
</form>
Enter fullscreen mode Exit fullscreen mode

app.component.ts

import { CommonModule } from '@angular/common';
import { Component, inject } from '@angular/core';
import {
  FormGroup,
  FormsModule,
  NonNullableFormBuilder,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';
import { ContatoFormType } from './contatoForm';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, FormsModule, ReactiveFormsModule],
  templateUrl: './app.component.html',
  styles: [],
})
export class AppComponent {
  private readonly formBuilder: NonNullableFormBuilder = inject(
    NonNullableFormBuilder
  ); // INJEÇÃO DE DEPENDÊNCIA

  public contatoForm!: FormGroup<ContatoFormType>;

  constructor() {
    this.contatoForm = this.formBuilder.group({
      nome: this.formBuilder.control(
        { value: '', disabled: false },
        {
          validators: [Validators.required],
        }
      ),
      email: this.formBuilder.control(
        { value: '', disabled: false },
        {
          validators: [Validators.required, Validators.email],
        }
      ),
      assunto: this.formBuilder.control(
        {
          value: '',
          disabled: false,
        },
        { validators: [Validators.required] }
      ),
      mensagem: this.formBuilder.control(
        { value: '', disabled: false },
        { validators: [Validators.maxLength(300)] }
      ),
    });
  }

  public submitForm(): void {
    if (this.contatoForm.valid) {
      const contato = this.contatoForm.getRawValue();

      console.log({ contato });
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

No nosso template HTML, colocamos os atributos que o formsModule disponibiliza dentro dos inputs, form e textarea. E no nosso component, fizemos a injeção de dependência do NonNullableFormBuilder (é um recurso disponível desde o Angular 14), onde nosso formulário não aceitará valores nulos.

Também criamos nosso FormGroup estritamente tipado (também é um recurso disponível desde o Angular 14, onde podemos tipar nossos formulários). No nosso caso:

import { FormControl } from '@angular/forms';

export type ContatoFormType = {
  nome: FormControl<string>;
  email: FormControl<string>;
  assunto: FormControl<string>;
  mensagem: FormControl<string>;
};
Enter fullscreen mode Exit fullscreen mode

e se esquecermos de colocar alguma propriedade criada em nosso type, a construção do formulário mostrará um erro:

Objeto com propriedades faltando

Erro sobre falta de propriedade

Acessibilidade em formulários

A acessibilidade nos formulários é crucial para garantir que todos os usuários, independentemente de suas habilidades ou limitações, possam interagir com eficácia e eficiência. Ao seguir as diretrizes da World Wide Web Consortium (W3C) e as diretrizes de acessibilidade para conteúdo da web (WCAG), as pessoas desenvolvedoras podem criar formulários que são acessíveis a uma ampla variedade de pessoas.

A W3C fornece padrões e recomendações para a criação de conteúdo acessível na web, enquanto a WCAG oferece diretrizes específicas para garantir a acessibilidade de conteúdos web para pessoas com deficiência. Ao implementar práticas recomendadas de acessibilidade nos formulários, os desenvolvedores podem garantir que os usuários tenham uma experiência inclusiva e equitativa ao preencher e enviar informações.

Demonstração de técnicas para tornar os formulários mais acessíveis

Dentro do nosso template HTML, podemos ver que o campo de entrada para email, está com o type="text". Podemos mudar isso para type="email", assim, validando auto completar em dispositivos móveis, e também para pessoas que fazem uso de dispositivos assistivos.

Outra coisa que pode ser percebida, é que nosso inputs não possuem um texto informativo sobre o que ele significa, então podemos atrelar um label a ele.
Para atrelarmos um label ao input, precisamos colocar um id como propriedade do input, e então, dentro da tag label, no atributo for, colocarmos o id do input. ex:

    <label for="email">Digite seu email</label>
    <input
      type="email"
      formControlName="email"
      required
      placeholder="exemplo@mail.com"
      id="email"
    />
Enter fullscreen mode Exit fullscreen mode

Podemos também, adicionar erros de validação dos nossos campos, utilizando o formgroup.controls.seuControl.errors, junto do touched e dirty, que são propriedades de validação exportadas pelo FormsModule
(Você pode ver mais sobre touched e dirty AQUI)

<div>
    <label for="email">Digite seu email</label>
    <input
      type="email"
      formControlName="email"
      required
      aria-required="true"
      aria-errormessage="emailInvalido"
      placeholder="exemplo@mail.com"
      aria-placeholder="exemplo de entrada: meuemail@mail.com"
      id="email"
      name="email"
    />
    @if (contatoForm.controls.email.errors && contatoForm.touched &&
    contatoForm.dirty) {
    <span id="emailInvalido" aria-live="assertive" role="alert"
      >Email inválido</span
    >
    }
  </div>
Enter fullscreen mode Exit fullscreen mode

Nesse caso, fizemos uso do aria-live="assertive" para, toda vez que o input estiver com um erro, o leitor de tela vai avisar à pessoa que o email digitado está invalido.
Mais sobre aria-live

Podemos também, adicionar validações de desabilitar/habilitar o botão do formulário, caso ele esteja com status INVALID.
Também, adicionaremos o aria-label para as pessoas que usam tecnologias assistivas, possam saber sobre o que se trata esse botão.

  <button
    type="button"
    [disabled]="contatoForm.invalid"
    [ariaDisabled]="contatoForm.invalid"
    aria-label="Enviar sua mensagem ao nosso time"
  >
  Nos envie uma mensagem
  </button>
Enter fullscreen mode Exit fullscreen mode

O restante do código você pode conferir no meu repositório github

Conclusão

Ao longo desse artigo, exploramos como funcionam formulários em aplicações web, e destacamos como a acessibilidade desempenha um papel fundamental na garantia de inclusão de pessoas. Criamos o formulário utilizando HTML puro, depois passamos ele para um componente Angular, adicionamos atributos necessários, módulos de Forms do próprio Angular, implantamos um formulário reativo que não aceita valores nulos, e adicionamos atributos ARIA-* dentro dos nossos inputs, a fim de melhorar a acessibilidade do formulário, tanto quanto outras tags que também nos auxiliaram a sinalizar melhor os campos de entrada.

É crucial que estejamos conscientes da importância da acessibilidade, e nos comprometermos a implementar práticas de design inclusivas em nossos projetos.

Obrigado!

Top comments (1)

Collapse
 
michelonsouza profile image
Michelon Souza

Pensa em um conteúdo top... Valeu @brunoredes ... Me ajudou bastante aqui
🚀