Formularios reactivos de Angular

Aprenderemos a cómo usar correctamente los formularios reactivos en Angular

13.11.2021 a las 10:25

Formularios reactivos de Angular

Aprenderemos a cómo usar correctamente los formularios reactivos en Angular


Sin duda, los formularios reactivos de Angular me costaron al principio, me resistía a estudiarlos bien porque pensaba que algo tan fácil como un formulario no debía de tratarse de forma tan "compleja" como lo trataba Angular.


Mi error era pensar que Angular los trataba de una forma compleja, cuando en realidad lo que hace es facilitarnos el trabajo, tan sólo hay que dedicar unos minutos a comprenderlo.

Demostración de lo que vamos a hacer

Imagina que quieres crear la carta de un restaurante donde cada plato (menuItem) puede contener un número indeterminado de precios, como pueden ser:

  • Tapa
  • Media ración
  • Ración
  • Plato único
  • 1 unidad
  • ...

Cada plato tendrá un nombre (name) que será obligatorio y como mínimo deberá tener 6 caracteres y por último, un array de objetos de precio que estará compuesto por una descripción (description) y un importe (price). El importe será obligatorio y tendrá un valor mínimo de 0.


  {
    "name":string,
    "prices":[
      {
        description:string,
        price:number
      },
      {
        description:string,
        price:number
      },
      ...
    ]
  }
  

A continuación puedes ver lo que crearemos, un formulario que representará cada menuItem donde podremos añadir un número indeterminado de precios.


Formulario válido: false


Código

Como verás tengo asignado el CSS en el HTML. Sé que no es una buena práctica, pero como era poco CSS lo he preferido hacer así para no tener otro archivo para el CSS.

Fichero HTML

Para darle una mejor apariencia he usado Angular Material.


    <form
        [formGroup]="formParent"
        style="border: grey 4px solid; padding: 1rem"
      >
        <p>Formulario válido: {{formParent.valid}}</p>
        <br>
        <div style="display: flex">
          <mat-form-field appearance="outline" style="max-width: 200px">
            <mat-label>Nombre</mat-label>
            <input
              type="text"
              autocomplete="off"
              matInput
              formControlName="name"
            />
            <mat-icon matSuffix>description</mat-icon>
            <mat-error>Mínimo 6 caracteres</mat-error>
          </mat-form-field>
          <button
            class="mat-elevation-z4"
            type="button"
            mat-raised-button
            color="accent"
            (click)="addPrice()"
            style="height: 63.5px"
          >
            Añadir precio <mat-icon class="iconBtn">euro</mat-icon>
          </button>
        </div>

        <div formArrayName="prices">
          <!-- fíjate que aquí no estamos asignando un formControlName, si no un formArrayName -->
          <div
            *ngFor="
              let price of getCtrl('prices', formParent)?.controls;
              index as indexFormChildPrice
            "
          >
            <div [formGroupName]="indexFormChildPrice">
              <mat-form-field appearance="outline" style="max-width: 200px">
                <mat-label>Descripción</mat-label>
                <input
                  type="text"
                  autocomplete="off"
                  matInput
                  formControlName="description"
                />
                <mat-icon matSuffix>description</mat-icon>
              </mat-form-field>
              <mat-form-field appearance="outline" style="width: 100px">
                <mat-label>Precio</mat-label>
                <input
                  type="number"
                  autocomplete="off"
                  matInput
                  formControlName="price"
                />
                <mat-icon matSuffix>dialpad</mat-icon>
              </mat-form-field>
            </div>
          </div>
        </div>
      </form>
  

Fichero TS


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

@Component({
  selector: 'app-angular-formularios-reactivos',
  templateUrl: './angular-formularios-reactivos.component.html',
  styles: [],
})
export class AngularFormulariosReactivosComponent implements OnInit {  

  public formParent: FormGroup = new FormGroup({}); //Creamos el formulario padre y lo inicializamos

  ngOnInit(): void {
    this.initFormParent();
  }

  private initFormParent(): void {
    //crea el formulario padre y lo asigna a la variable formParent
    this.formParent = new FormGroup({
      name: new FormControl('', [Validators.required, Validators.minLength(6)]),
      prices: new FormArray([], [Validators.required]),
    });
  }

  private initFormPrice(): FormGroup {
    //devuelve un formulario hijo
    return new FormGroup({
      description: new FormControl(''),
      price: new FormControl('', [Validators.required, Validators.min(0)]),
    });
  }

  public addPrice(): void {
    //método que al pulsar en el botón de añadir precio añadirá un nueve elemento price al array prices
    const refPrices = <FormArray>this.formParent.get('prices');
    refPrices.push(this.initFormPrice());
  }

  public getCtrl(key: string, form: FormGroup) {
    return <FormArray>form.get(key); //con esto devolvemos la propiedad key del formulario form
  }
}
  
Hasta luego,

Servicios

Software

IoT

Digitalización

Aplicaciones móviles

Consultoría

fjmduran.com v0.1.2