# Formulário Simples

## Pré-requisitos

* Leia a página anterior Reactive Forms (Formulários Reativos)
* Acesse [angular-forms-example](https://github.com/arthur-lima-dev/angular-forms-example) e faça download do projeto
* Leia atentamente o README.md

### Estrutura de páginas

* Reactive Forms
* Formulário Simples

Atualmente são apenas duas páginas, mas ao longo dos tutoriais outras serão desenvolvidas.&#x20;

![Demonstração do projeto](/files/-M8bGgrd1QIcUOKGwcrO)

## Inicializando a aplicação

```
$ ng-serve
```

{% hint style="success" %}
&#x20;Após carregar acesse <http://localhost:4200/> leia atentamente o conteúdo da página Reactive Forms, em seguida navegue para a página Formulário Simples, leia e realize testes no formulário.
{% endhint %}

{% hint style="warning" %}
Os passos acima informados são extremamente necessários para o completo entendimento deste tutorial.
{% endhint %}

## Arquitetura

{% hint style="warning" %}
Leia os comentários de cada método dos arquivos presentes neste projeto, isso irá ajudar muito na compreensão do que foi feito.
{% endhint %}

Levando em consideração que o ".html" seja o ponto de partida para visualizarmos uma página, quero propor a seguinte abordagem aqui, que será mostrar do arquivo mais interno ao mais externo. Ou seja, vamos começar pelo serviço, passando para o componente até chegar na página ".html".

### abstract-form-builder.ts

É esta classe que irá fazer toda a "magia" na criação do objeto ***FormGroup***. Em resumo sua função é criar o ***FormGroup*** do tipo  **\<T>** do qual será passado pela classe que for sua herdeira. Seu método principal é o **construirFormGroup()** além de criar o ***FormGroup*** ele cria uma referência local através do método  **atualizarReferenciaFormulario()** capturando os ***FormControls*** e passando para a variável local **formControls**  do tipo **\<T>**. Além disso são inicializadas as validações padrão dos ***FormControls*** criados, quando houverem.&#x20;

{% code title="abstract-form-builder.ts" %}

```typescript
import {Injectable} from "@angular/core";
import {AbstractControl, FormBuilder, FormGroup} from "@angular/forms";

@Injectable()
export abstract class AbstractFormBuilder<T> {

  formControls: T;
  
  constructor(
    private formBuilder: FormBuilder
  ) { }

  /**
   * Quem implementar será obrigado a informar se tem alguma validação 
   * padrão a ser feita no form
   */
  abstract inicializarValidacoesDefault();

  /**
   * Aqui Passamos um objeto com os atributos que queremos para o 
   * nosso formulário, neste método será criado um FromGroup
   *
   * @param objetoReferencia Objeto que dará inicio a magia
   * @return: FormGroup
   */
  construirFormGroup(objetoReferencia: T): FormGroup{
    const form = this.formBuilder.group(objetoReferencia);
    this.atualizarReferenciaFormulario(form, objetoReferencia);
    this.inicializarValidacoesDefault();

    return form;
  }

  /**
   * Captura os controls criados e passam para um objeto como tipo T,
   * desta forma evita-se o comum uso de "this.formGroup.get('nomeAtributo')"
   * e passa-se a usar um obejeto com os atributos necessários
   *
   * @param form
   * @param objetoReferencia
   */
  atualizarReferenciaFormulario(form: FormGroup, objetoReferencia: T){
    Object.entries(objetoReferencia).forEach(field => {
      const label = field[0];
      objetoReferencia[label] = form.controls[label];
    });
    this.formControls = objetoReferencia;
  }

  /**
   * Ao receber um Control seus validators serão substituidos pelos informados
   *
   * @param control
   * @param novoValidador
   */
  atualizarValidadores(control: AbstractControl, novoValidador: any | any[]){
    control.setValidators(novoValidador);
    control.updateValueAndValidity();
  }
}

```

{% endcode %}

### formulario-simples-builder-service.ts

Reparem que criei uma classe chamada **PessoaVM** (é ela que será o tipo **\<T>**) no mesmo arquivo, mas se preferir pode colocar dentro de um arquivo próprio. Sua função é ser um modelo para nosso formulário, lembre-se que um dos problemas apontados no artigo anterior foi justamente o fato dos atributos serem criados em tempo de execução. Agora não mais! :smiley:&#x20;

{% code title="formulario-simples-builder-service.ts" %}

```typescript
import {Injectable} from "@angular/core";
import {AbstractFormBuilder} from "src/app/shared/form-builer/abstract-form-builer";
import {Validators} from '@angular/forms';

/**
 * Model, DTO, VM... Tanto faz!
 * Independente da sigla que for usar a idea é ter um objeto que pode 
 * ser usado como coringa.
 * Vamos entender melhor no artigo
 */
export class PessoaVM {
  constructor(
    public nome?: any,
    public apelido?: any,
    public altura?: any
  ) { }
}

/**
 * A ideia é armazenar nesse serviço todas as regras de validações do formulário
 */
@Injectable()
export class FormularioSimplesBuilderService extends AbstractFormBuilder<PessoaVM> {

  /** Podem ser um ou vários validator como para o campo default */
  private alturaValidatorDefault = Validators.required;

  /**
   * Regra de negócio 1: os campos nome e altura são de preenchimento obrigatório
   */
  inicializarValidacoesDefault() {
    this.atualizarValidadores(this.formControls.nome, Validators.required);
    this.atualizarValidadores(this.formControls.altura, this.alturaValidatorDefault);
  }

  /**
   * Regra de negócio 2: Quando o apelido for preenchido então a altura passa 
   * a ter uma validação mínima de 1.20 e máxima de 2.20.
   * Caso contrário então passa o validator default
   *
   * obs: repare no this.formControls.apelido.value "apeli passou a ter um novo
   * atributo o '.value' isso porque no objeto 'formControls' ele tem uma
   * referência de um FormControl".
   */
  atualizarComportamentoControlAltura() {
    const alturaValidators = this.formControls.apelido?.value ? [
      this.alturaValidatorDefault,
      Validators.min(1.20),
      Validators.max(2.20)
    ] : this.alturaValidatorDefault;

    this.atualizarValidadores(this.formControls.altura, alturaValidators);
  }
}

```

{% endcode %}

O Serviço **FormularioSimplesBuilderService** é filho de **AbstractFormBuilder**, ou seja, seus comportamentos serão herdados e é nele que devemos aplicar todas as regras de validação do formulário. Isso o torna um serviço especializado em regras de negócio especificas do formulário que estamos desenvolvendo o que irá tornar o código mais legível e com baixo acoplamento.

A beleza de guardarmos uma referência dos ***FormControls*** dentro do objeto **formControls** é que não precisaremos usar o método **get()** do ***FormGroup*** passando uma string e correndo o risco de ter que replicar esse código diversas vezes. Dependendo da quantidade de regras de validação de um determinado campo esse **get()** irá sair caro pra você e é nesse ponto que está o campo minado. :fire:&#x20;

{% code title="campo-minado-component.ts" %}

```typescript
  /**
  * Imagina só sua classe component cheia desses métodos get('altura') pra cá
  * get('altura') pra lá e de repente o atributo muda de "altura" para 
  * "alturaDaPessoa" você vai precisar de uma boa IDE para te ajudar arrumar isso.
  */
  modificarComportamentoFormulario() {
    this.pessoaFormGroup.get('altura').setValidators(
      this.pessoaFormGroup.get('apelido').value ? [
        Validators.required,
        Validators.min(1.20),
        Validators.max(2.20)
        ] : Validators.required
    );
  }
```

{% endcode %}

Vamos usar um objeto como referência que é melhor né!? Usamos o **this.formControls** objeto que foi herdado de **AbstractFormBuilder** dai colocamos **this.formControls.altura**  se mudar já vai aparecer como erro de compilação, entretanto, será mais difícil isso acontecer pois a maioria das IDE's são eficientes no seu ***Refactor > Rename***  e conseguem atualizar todas as referências.

### formulario-simples.component.ts

Nesta classe iremos injetar o **FormularioSiplesBuilderService**, usar o método **construirFormGroup()** e armazenar numa variável local do tipo ***FormGroup***. Além disso a classe não tem mais preocupação nenhuma em como serão feitas as validações do formulário, mas apenas em quando serão feitas.&#x20;

{% code title="formulario-simples.component.ts" %}

```typescript
import {Component, OnInit} from '@angular/core';
import {FormGroup} from "@angular/forms";
import {FormularioSimplesBuilderService, PessoaVM} from "./formulario-simples-builder-service";

@Component({
  selector: 'app-formulario-simples',
  templateUrl: './formulario-simples.component.html',
  styleUrls: ['./formulario-simples.component.css'],
  providers: [FormularioSimplesBuilderService]
})
export class FormularioSimplesComponent implements OnInit {

  pessoaFormGroup: FormGroup;
  exibeTextoSubmissao: boolean = false;

  constructor(
    private formularioSimplesBuilderService: FormularioSimplesBuilderService
  ) {
  }

  ngOnInit(): void {
    this.inicializarFormulario();
  }
  
   /**
   * Dica: sempre bom separar bem os métodos e suas responsabilidades, alguns 
   * programadores costumam criar métodos gigantes que fazem milhares de 
   * coisas ao mesmo tempo. Não seja essa pessoa!
   */
  private inicializarFormulario() {
    this.pessoaFormGroup = this.formularioSimplesBuilderService.construirFormGroup(new PessoaVM());
  }

  /**
   * Retornar os valores do formulário no formato Json
   */
  verificarValoresFormulario() {
    return JSON.stringify(this.pessoaFormGroup?.value, null, 2);
  }

  /**
   * Este método esta sendo chamado pelo html toda vez que é digitado alguma 
   * iformação no campo apelido.As regras de como os campos devem se comportar 
   * estão no FormulárioSimplesBuilderService
   */
  modificarComportamentoFormulario() {
    this.formularioSimplesBuilderService.atualizarComportamentoControlAltura();
  }

  /**
   * Nesse método poderiamos chamar métodos que irão tratar as informações 
   * adquiridas no formulário e em seguinda utilizar um serviço para enviar 
   * para o back-end (Quem sabe a gente não faz isso um dia)
   */
  submeterFormulario() {
    this.exibeTextoSubmissao = true;
  }

}

```

{% endcode %}

O método **modificarComportamentoFormulário()** será executado quando o campo apelido for modificado então ele irá chamar o método **atualizarComportamentoControlAltura(),** pois esse sim sabe como manipular as validações. Olha que maravilha, o componente não precisa se preocupar em como fazer ele só delega e nosso Serviço especializado se encarrega de mostrar para o que veio. :face\_with\_monocle:&#x20;

### formulario-simples.component.html

Finalmente o "html" aqui não temos nada novo, o formulário foi apresentado assim como qualquer exemplo que podemos encontrar no site do Angular.

{% code title="formulario-simples.component.html" %}

```markup
<!-- FORMULÁRIO - estão sendo utilizadas classes de estilização do Bootstrap 
  e campos do Angular Material -->
<div class="container-fluid d-flex justify-content-center">
  <div class="card bg-light">
    <div class="card-header text-center">
      <h2 class="card-title">Cadastrar Pessoa</h2>
    </div>

    <div class="card-body">
      <form class="form-group" [formGroup]="pessoaFormGroup" 
        (ngSubmit)="submeterFormulario()">
        <div class="row">
          <mat-form-field class="col">
            <mat-label>Nome</mat-label>
            <input matInput type="text" formControlName="nome">
          </mat-form-field>
        </div>

        <div class="row">
          <mat-form-field class="col-9">
            <mat-label>Apelido</mat-label>
            <input (keyup)="modificarComportamentoFormulario()" 
              formControlName="apelido" matInput type="text">
          </mat-form-field>

          <mat-form-field class="col-3">
            <mat-label>Altura</mat-label>
            <input matInput type="number" formControlName="altura">
          </mat-form-field>
        </div>

        <div class="text-right mt-2">
          <button mat-raised-button class="btn btn-success" type="submit"
                  [disabled]="pessoaFormGroup.invalid">Salvar
          </button>
        </div>
      </form>
    </div>

    <div class="alert alert-info" role="alert">
      <div class="row d-flex justify-content-center">
        <strong>Valores (JSON)</strong>
      </div>

      <div class="row d-flex justify-content-center">
        <pre>{{verificarValoresFormulario()}}</pre>
      </div>
    </div>

  </div>
</div>


<div class="container-fluid mt-2">
  <div *ngIf="exibeTextoSubmissao" class="alert alert-danger">
    <button mat-mini-fab color="warn" class="float-right" 
      (click)="exibeTextoSubmissao = !exibeTextoSubmissao">
      <mat-icon>close</mat-icon>
    </button>

    <h4 class="alert-heading">
      <span class="material-icons">device_unknown</span>️
      Vish!! Acho que tá faltando alguma coisa
    </h4>
    <hr>

    <p>
      Vi que você tentou salvar, só que eu não fiz essa parte. <span class="material-icons">pest_control</span>
      <br>
      Caso queira avançar sugiro que crie um serviço para enviar as informações do front-end
      para o back-end através de uma requisição http.
    </p>

    <p>Ps: se valer de alguma ajuda eu já fiz um método chamado "submeterFormulario" e acho você pode usá-lo para
      salvar.
      <br>
      Boa sorte! <span class="material-icons">directions_run</span></p>
  </div>

  <div class="alert alert-warning" role="alert">
    <h4>Regras de negócio aplicadas:</h4>
    <ul>
      <li>O Campos nome e altura são de preenchimento obrigatório</li>
      <li>Ao preencher o apelido o campo altura passa a ter um valor mínimo de 1.20 e máximo de 2.20</li>
    </ul>
    <hr>

    <h4><span class="material-icons">contact_support</span> Dúvidas</h4>
    <h5 class="alert-heading"> - Quem vai pedir um cadastro com uma regra tão estranha assim?</h5>
    <p>
      De fato a regra da altura é estranha, mas acredite quando eu digo que seu cliente poderá
      ter requisitos bem peculiares. <span class="material-icons">sentiment_satisfied</span>
    </p>

    <h5 class="alert-heading"> - Onde foram parar os valores digitados?</h5>
    <p>
      Estão no objeto do tipo FormGroup e também estão no objeto formControls da classe FormularioSimplesBuilderService
      que criamos para facilitar nossa vida. Vamos entender melhor sobre isso durante o tutorial.
    </p>
  </div>
</div>

```

{% endcode %}

{% hint style="warning" %}
Reitero a importância de você abrir o projeto, executar e ler seu conteúdo, para adquirir um completo entendimento acerca deste artigo. Se não o fez, volte e faça agora, eu espero. :hourglass\_flowing\_sand:&#x20;
{% endhint %}

{% hint style="danger" %}
Caso tenha gostado da solução e deseje implementá-la no seu projeto sugiro você verificar se os objetos recebidos nos métodos são nulos. É importante, mas no exemplo eu não fiz. :sweat\_smile:&#x20;
{% endhint %}

**Obrigado por chegar até aqui!**:partying\_face: **Aguardem para mais artigos.**

![O Autor](/files/-M7s-eacEUwFodoje4vE)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers-friends.gitbook.io/blog/angular/reactive-forms/formulario-simples.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
