¿Qué aprenderá?

Al finalizar este tutorial el estudiante estará en capacidad de implementar un formulario básico en Angular para crear un recurso de la aplicación.

¿Qué construirá?

Específicamente, se utilizará la librería Angular de formularios reactivos (Reactive Forms) para definir un formulario en un componente de creación de un recurso. Este formulario tendrá la definición de los elementos que se quieren capturar en la interacción con el usuario así como sus validaciones básicas. Igualmente, se muestra cómo utilizar el formulario en el template HTML y cómo se puede desplegar mensajes de error y mensaje de confirmación cuando el usuario hace un submit del formulario.

El resultado del tutorial es una aplicación que tiene un formulario básico que se muestra en la siguiente figura. Hay tres campos:

¿Qué necesita?

Para poder realizar este taller Ud. debe haber tener claro:

  1. Cuál es la estructura de un proyecto de Angular: módulos, componentes, servicios
  2. Entender la funcionalidad del Angular-Cli

En este tutorial vamos a crear una aplicación Angular con un componente para crear un formulario.

Crear el proyecto

Utilizando Angular-cli desde VSCode cree un nuevo proyecto Angular.

Crear el módulo nuevo

Sobre la carpeta app de su proyecto seleccione la opción de Angular Generator (angular-cli) y allí Módulo. Defina el nombre de su módulo como client

Asociar el nuevo módulo en la aplicación principal.

Vaya al archivo app.module.ts y agregue el nuevo módulo. Debe hacer dos cosas: 1) Importar utilizando el nombre de la clase del nuevo módulo y asociarlo con el archivo físico. Asegúrese de escribir correctamente la ruta del archivo físico. 2) Incluir en el atributo imports del decorador del módulo, el nombre del módulo nuevo.

Crear el componente dentro del módulo

Vaya a la carpeta del módulo client, clic derecho Angular Generator/Componente

Dele el nombre client-create

Asociar el componente con el módulo

Angular-cli crea la declaración del componente en el módulo. Sin embargo, se debe agregar una línea con el atributo exports para que este componente se pueda ver desde la aplicación principal.

Importar en el módulo la librería de formularios

La librería que vamos a utilizar para manejar los formularios se llama ReactiveFormsModule y se debe incluir e importar en el módulo donde se definen componentes que tendrán formularios. En este tutorial, nuestro módulo es ClientModule

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { ClientCreateComponent } from './client-create/client-create.component';

@NgModule({
  imports: [
    CommonModule, ReactiveFormsModule
  ],
  declarations: [ClientCreateComponent],
  exports:[ClientCreateComponent]
})
export class ClientModule { }



      

Vamos a llamar el componente de crear un cliente utilizando su selector desde el componente principal.

En el archivo del componente principal "app.component.html" borre lo que hay y pegue el selector entre tags de apertura y cierre.

Angular cuenta con librerías para manejar los formularios. Estas librerías permiten crear objetos que contienen los campos en el formulario y que serán desplegados en la vista. La librería se encarga de mantener la relación entre los campos de entrada (input) que despliega la vista y los datos en el componente.

Estas librerías debemos importarlas en el componente de creación del recurso.

import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
...

En nuestro ejemplo, vamos a implementar un formulario para crear un nuevo cliente. En el siguiente código del componente vamos a declarar una variable clientForm de tipo FormGroup.

import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { ToastrService } from "ngx-toastr";
import { Client } from '../client';


@Component({
  selector: "app-client-create",
  templateUrl: "./client-create.component.html",
  styleUrls: ["./client-create.component.css"]
})
export class ClientCreateComponent implements OnInit {
  clientForm: FormGroup;
...
}

Un objeto formulario es de tipo FormGroup y contiene un conjunto de objetos FormControl donde cada uno representa un campo del formulario para que el usuario ingrese valores.

Vamos a inicializar la forma clientForm con los elementos que la componen. Esto lo hacemos en el método ngOnInit del componente. Por cada campo de entrada, definimos un nombre y sus propiedades de validación si es que las tiene:

 ngOnInit() {
    this.clientForm = this.formBuilder.group({
      name: ["", [Validators.required, Validators.minLength(2)]],
      address: ["", Validators.required],
      email: ["", [Validators.required, Validators.email]]
    });

En el formulario clientForm, estamos definiendo tres campos:

La validaciones significan que si cualquiera de esas validaciones no se cumple, por ejemplo, el campo address no tiene valor, el formulario es inválido. En este ejemplo solo estamos utilizando validaciones predefinidas. También podemos definir nuestras propias validaciones.

La vista del componente, es decir el código html, va en el archivo client-create.component.html.

El formulario que vamos a construir es el siguiente:

Este formulario es un elemento HTML form que contiene los tres elementos inputs con sus respectivos label y el código para desplegar los mensajes de error, debajo de cada elemento, si no se cumplen las reglas de validación. Antes de cerrar la etiqueta form, se incluye tanto en botón de submit para crear el cliente, como el botón de cancel para cancelar el formulario. Queremos, además, que el botón de submit esté activo sólo si el formulario es válido.

Vamos a explicar el HTML gradualmente sin detenernos en el detalle de los estilos. La siguiente figura muestra la organización del HTML. Existe una etiqueta form que contiene, por cada campo en el formulario, un grupo de etiquetas para el despliegue y las validaciones. AL final está el código para mostrar los botones:

Etiqueta form

La etiqueta form contiene la referencia la modelo del formulario, es decir a la variable clientForm que se creó en la clase del componente. También contiene la directiva Angular (ngSubmit) que tiene por valor la expresión que será evaluada cuando el usuario hace clic en el botón de submit. En este caso, preguntamos si el formulario es válido y se se llama la función createClient(clientForm.value). Esta función debemos definirla en la clase del componente. El detalle de este código se muestra a continuación:

<form [formGroup]="clientForm" (ngSubmit)="!clientForm.invalid && createClient(clientForm.value)">
...  
</form>

Create y Cancel

Veamos ahora el código asociado con los botones que va antes de cerrar la etiqueta

</form>.

El botón para crear el cliente, está deshabilitado si el formulario no es válido [disabled]="!clientForm.valid"

Ese botón es de tipo type="submit" lo que implica que cuando el usuario le hace clic se ejecutará el (ngSubmit) del formulario. En este caso se llamará a crear el cliente con los valores recolectados en los campos que se encuentra en clientForm.value.

El botón de cancelar tiene una directiva (click) que invoca la función cancelCreation() que también debe estar definida en la clase del componente.

<button type="submit" class="btn btn-primary" [disabled]="!clientForm.valid">Create</button>                
                <button type="button" class="btn btn-danger ml-3" (click)="cancelCreation()">Cancel</button>

Campos en el formulario

Por cada campo tenemos un label, una etiqueta input y una etiqueta por cada validación que fue definida en la configuración del formulario en la clase del componente.

La siguiente figura muestra lo que se despliega si el usuario ingresa mal la información en todos los campos de acuerdo con las reglas definidas. El nombre tiene menos de 2 letras,m la dirección no tiene valor y el email no tiene el formato correcto. Note que el botón Create no está habilitado.

Veamos un ejemplo con el campo name que fue definido de la siguiente forma en el componente, con dos validaciones:

this.clientForm = this.formBuilder.group({
      name: ["", [Validators.required, Validators.minLength(2)]],
     ...
    });

El HTML correspondiente es el siguiente: el label, el input y las dos validaciones:

<div class="form-group mx-sm-3 mb-2">
                <label for="name">
                    Name
                </label>
                <input novalidate id="name" class="form-control" formControlName="name" placeholder="Your Full Name">
                <div class="alert alert-danger alert-dismissible fade show"
                    *ngIf="clientForm.get('name').hasError('required') && clientForm.get('name').touched">
                    Name required
                </div>
                <div class="alert alert-danger alert-dismissible fade show"
                    *ngIf="clientForm.get('name').hasError('minlength')">
                    Name too short
                </div>
            </div>

Input

Para la etiqueta input, tenemos que el significado de cada atributo es el siguiente:

novalidate

Se define para impedir que se ejecuten validaciones de html normales dado que las validaciones se definen en la forma.

id="name"

Identifica el campo

class="form-control"

Formato de bootstrap para que quede el label y el input uno debajo del otro

formControlName="name"

El nombre del campo en el objeto clientForm definido en el componente

placeholder="Your Full Name"

El valor que va a aparecer por defecto

Validaciones

Procesar una validación consiste en definir un condicional utilizando la directiva de Angular *ngIf para consultar si hay el error y en ese caso, desplegar el mensaje al usuario.

En este ejemplo queremos saber si hay error en la validación sobre la longitud del texto.:

*ngIf="clientForm.get('name').hasError('minlength')

Veamos el código:

<div class="alert alert-danger alert-dismissible fade show"
                    *ngIf="clientForm.get('name').hasError('minlength') && clientForm.get('name').touched">
                    Name too short
                </div>

También se agrega clientForm.get('name').touched para saber si el campo fue efectivamente tocado por el usuario.

De nuestro formulario las consultas al error son:

Definición el el componente

*ngIf en el HTML

Validators.required

clientForm.get('address').hasError('required')

Validators.minLength(2)

clientForm.get('name').hasError('minlength')

Validators.email

clientForm.get('email').hasError('email')

Cuando el formulario es válido y el usuario hace clic en el botón Create, estamos invocando el método createClient(clientForm.value) que debe estar definido en la clase del componente. En nuestro tutorial tenemos el siguiente código, que imprime el cliente que se va a crear en la consola, llama a showSucess() que utiliza el toastr y resetea el formulario. Entre comentarios lo que sería el código completo si llamamos el servicio que invoca el HttpClient de Angular:

createClient(newClient: Client) {
    // Process checkout data here
    console.warn("el cliente fue creado", newClient);
    this.showSuccess(newClient);

    //-----------------------------------------------------------------
    // this.clientService.createClient(newClient).subscribe(client => {
    //   this.clientes.push(client);
    //  this.showSuccess(newClient);
    // });
    //------------------------------------------------------------------
    this.clientForm.reset();

  }

El resultado final utilizando el toastr es el que se presenta en la figura:

Cuando el usuario cancela debemos decidir si queremos enviarle un mensaje al usuario utilizando el toastr, debemos resetear el formulario y decidir a dónde queremos navegar.

En este tutorial que solo tiene el formulario no navegamos a ningún lado, solo reseteamos el formulario.

cancelCreation() {
    console.log("Cancelando ...");
    this.clientForm.reset();
  }