Desenvolvendo Formulários Reativos e Acessíveis no Angular: Um Guia Passo a Passo
Tags: braziliandevs, angular, a11y, frontend
Published: 2024-03-01 16:00:54

### Glossário

- [Introdução](#intro)

  - [O que é um formulário HTML](#formsHtml);
  - [Relevância da acessibilidade dentro de um formulário](#formWithA11y);
- [Formulários tradicionais](#commonForms)
  - [Como os formulários são tradicionalmente criados em aplicações web.](#defaultDevelopedForms)
  - [Exemplo de código HTML para um formulário simples.](#formCodeExample)
- [Formulários Reativos em Angular](ngReactiveForms)
  - [Criação de um formulário reativo no Angular.](#ngReactiveFormsCreate)
  - [Implementando nosso formulário, em um componente Angular](#ngOurFormInReactiveForms).
- [Acessibilidade em formulários](#formsA11y)
- [Demonstração de técnicas para tornar os formulários mais acessíveis](#a11yTechniques)
- [Conclusão](#articleConclusion)

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**

##

<h3 id="intro">Introdução</h3>

<h4 id="formsHtml">O que é um formulário HTML?</h4>
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.</p>


<h4 id="formWithA11y">Relevância da acessibilidade dentro de um formulário</h4>
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.


##

<h3 id="commonForms">Formulários tradicionais</h3>

<h4 id="defaultDevelopedForms">Como os formulários são tradicionalmente criados em aplicações web.</h4>
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](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/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.

```HTML
<input type="text" placeholder="algum exemplo aqui" />
```

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

```HTML
<button type="submit">Enviar formulário</button>
```

que, quando clicado, <abbr translate="yes" title="envia">submete</abbr> os dados para processamento pelo servidor.

<h4 id="formCodeExample">Exemplo de código HTML para um formulário simples.</h4>

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.

```HTML
<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>
```

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](https://angular.dev/guide/forms#setup-in-reactive-forms)

### Inicializando um projeto minimalista em Angular

Tenha instalado em sua máquina, o [NodeJS](https://nodejs.org) ou [bun](https://bun.sh/) (Apenas versões a partir do 17 do Angular suportam o bun)

- Instalando o angular cli:

```bash
$ npm i -g @angular/cli@latest
```

- Criando o projeto minimalista

```bash
$ ng new <NOME_DO_PROJETO> --minimal
```

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


##

<section role="region" aria-labelledBy="ngReactiveForms">
<h3 id="ngReactiveForms">Formulários Reativos em Angular</h3>
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**

<article role="region" aria-labelledBy="ngReactiveFormsCreate">

<h4 id="ngReactiveFormsCreate">Criação de um formulário reativo no Angular</h4>

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](https://angular.io/guide/reactive-forms#grouping-form-controls)

form.component.ts

```typescript
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(''),
  });
}
```

form.component.html

```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>
```

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](https://angular.io/guide/binding-syntax#types-of-data-binding), adicionando a propriedade do nosso component.ts, `profileForm`, para relacionarmos o formulário do template HTML, com o FormGroup criado dentro do componente.

<h4 id="ngOurFormInReactiveForms">Implementando nosso formulário, em um componente Angular</h4>

app.component.html

```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>
```

app.component.ts

```typescript
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 });
    }
  }
}
```

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](https://v14.angular.io/api/forms/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:

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

export type ContatoFormType = {
  nome: FormControl<string>;
  email: FormControl<string>;
  assunto: FormControl<string>;
  mensagem: FormControl<string>;
};
```

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


![Objeto com propriedades faltando](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wn28worai1cbfx03ac3u.png)


![Erro sobre falta de propriedade](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wn93tj9jy8pzczjduxga.png)


##

<h3 id="formsA11y">Acessibilidade em formulários</h3>

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.</p>

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.
</p>

##

<h3 id="a11yTechniques">Demonstração de técnicas para tornar os formulários mais acessíveis</h3>

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:
```HTML
    <label for="email">Digite seu email</label>
    <input
      type="email"
      formControlName="email"
      required
      placeholder="exemplo@mail.com"
      id="email"
    />
```

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](https://vidafullstack.com.br/angular/o-que-e-reactive-forms-angular/))

```HTML
<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>
```
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](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions)

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](https://dev.to/brunoredes/aprimorando-acessibilidade-em-botoes-no-html-42me) para as pessoas que usam tecnologias assistivas, possam saber sobre o que se trata esse botão.

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

O restante do código você pode conferir no meu [repositório github](https://github.com/brunoredes/form-error-a11y)


## 

<h3 id="articleConclusion">Conclusão</h3>

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.
</p>

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

Obrigado!