Introducción
Contenido
Una de las características más comunes en cualquier aplicación web es proporcionar un formulario a los usuarios para que ingresen algunos datos. Utiliza formularios a diario para iniciar sesión, registrarse, realizar pedidos, etc.
Procesar las entradas del usuario antes de validar puede tener graves consecuencias. Puede terminar almacenando datos no válidos como una fecha incorrecta, correo electrónico, edad, etc. También podría ser un problema de seguridad debido a ataques como Secuencias de comandos entre sitios (XSS).
La forma tradicional de validar formularios HTML es mediante JavaScript o JQuery. Desafortunadamente, este enfoque garantiza un montón de código.
Angular, al ser un marco completo, ha brindado un excelente soporte para validar las entradas de los usuarios y mostrar mensajes de validación. Tiene muchos validadores integrados de uso común que puede aprovechar, o incluso puede escribir sus validadores personalizados.
Formas en Angular
Un formulario angular es un formulario HTML normal con pocas características adicionales. Para cada campo (entrada, radio, selección, etc.) en el formulario, necesitamos un objeto del FormControl
clase. los FormControl
El objeto proporciona información sobre ese campo. Sus value
, si el valor es valid
, y si no es valido cuales son las validaciones errors
etc.
También proporciona el estado del campo, como touched
, untouched
, dirty
, pristine
etc.
Del mismo modo, un FormGroup
es la colección de la FormControl
objetos. Cada forma angular tiene al menos una FormGroup
. Puede decidir tener varios FormGroup
s en casos de uso como separar las secciones de manejo de detalles personales y detalles profesionales de un formulario de registro de usuario.
Todas las propiedades de un FormGroup
(valid
, error
, etc.) también está disponible para FormControl
. Por ejemplo, el valid
propiedad de un FormControl
volverá true
me caigo FormControl
las instancias son válidas.
Entonces, para agregar validación a una forma angular, necesitamos dos cosas:
- Al menos uno
FormGroup
objeto para el formulario - UN
FormControl
objeto para cada campo en el formulario
Hay dos formas diferentes de crear estos objetos de control. Podemos proporcionar algunas directivas en la plantilla del formulario y Angular puede crear tales controles bajo el capó para nosotros. Los formularios creados de esta manera se denominan formularios basados en plantillas.
Si tenemos algunos casos de uso especiales y queremos más control sobre el formulario, podemos crear explícitamente tales objetos de control. Los formularios creados de esta manera se denominan formas reactivas.
Formularios basados en plantillas
En formularios basados en plantillas, aplicamos el ngModel
directiva para cada campo de la plantilla. Angular crea un FormControl
objeto bajo el capó para cada uno de esos campos y asócielo con el campo respectivo:
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name"
ngModel name="name">
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username"
ngModel name="username">
</div>
Nota: Con ngModel
, se requiere proporcionar el name
atributo o definir el FormControl
como «independiente» en ngModelOptions
, de lo contrario Angular arrojará un error.
También en app.module.ts
necesitarías agregar FormsModule
a la matriz de importaciones:
import { FormsModule } from '@angular/forms';
// ...some other imports
imports: [
//...some other imports
FormsModule
]
Validación en formularios basados en plantillas
Angular ha proporcionado algunos validadores incorporados para validar casos de uso comunes. Para utilizar validadores integrados, necesitaría aplicar atributos de validación a cada campo de formulario donde desee alguna validación. Estos atributos de validación son los mismos que los atributos de validación HTML5 normales como required
, minlength
, maxlength
, etc. Bajo el hod, Angular ha proporcionado directivas para hacer coincidir estos atributos con las funciones de validación definidas en el marco Angular.
Siempre que un FormControl
Cuando cambia el valor, Angular genera una lista de errores de validación al ejecutar la validación. Si la lista está vacía, significa que es un estado válido; de lo contrario, es un estado no válido.
Digamos que queremos ponerle las siguientes validaciones:
- Como los campos Nombre y Nombre de usuario tienen la
required
atributo, queremos mostrar un mensaje de validación si este campo se deja vacío. - El campo Nombre debe tener un valor cuyo
minlegth
ymaxlength
debe tener 2 y 30 caracteres respectivamente. - Si el nombre de usuario tiene espacios, muestre un mensaje de nombre de usuario no válido.
Para cada control de formulario en el que queremos agregar validación, necesitamos agregar atributos de validación apropiados y exportar ngModel
a una variable de plantilla local:
<input type="text" class="form-control" id="name"
required maxlength="30" minlength="2"
ngModel name="name" #name="ngModel">
En el ejemplo anterior, hemos utilizado los siguientes validadores integrados: required
, minlength
y maxlength
.
Podemos usar la variable de plantilla name
en la plantilla para comprobar los estados de validación de los validadores utilizados:
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.errors.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
Name cannot be more than 30 characters long.
</div>
<div *ngIf="name.errors.minlength">
Name must be at least 2 characters long.
</div>
</div>
Como hemos usado una declaración condicional para representar la primera div
, solo se mostrará si el estado del validador integrado es invalid
. Hemos explicado al comienzo de la sección cómo se determina el estado como valid
o invalid
.
Del mismo modo, el interior div's
se mostrará solo si la variable de plantilla name
tiene una propiedad errors
y el errors
La propiedad tiene una de las siguientes propiedades: required
, minlength
y maxlength
y la identificación del valor de la propiedad true
. Ya hemos discutido cómo la variable de plantilla se une a la ngModel
directiva y recibe estas propiedades cada vez que hay algún cambio en el control de formulario y después de que Angular ejecuta la validación para ese campo.
Nota: Es importante comprobar dirty
y touched
afirma, de lo contrario, el mensaje de error se mostrará la primera vez que se cargue la página, lo cual es malo para la experiencia del usuario. Necesitamos que el mensaje de validación se muestre en una de las siguientes condiciones:
- El usuario cambia algún valor, es decir, el campo está sucio (
formControlObject.dirty
) - El usuario usa la pestaña o hace clic para cambiar el enfoque a algún otro elemento, es decir, se tocó el campo (
formControlObject.touched
)
Si desea consultar una lista completa de los validadores integrados de Angular, puede seguir las API de validadores.
Te puede interesar:Cómo arreglar «ADVERTENCIA: ARCHIVO DE CLAVE PRIVADA NO PROTEGIDO!» en Mac y LinuxEscribir un validador personalizado
A veces, los validadores integrados pueden no cubrir su caso de uso exacto. En este caso, es posible que deba crear su función de validación personalizada.
Una función de validación implementa la ValidatorFn
interfaz, lo que significa que debe tener la firma:
interface ValidatorFn {
(control: AbstractControl): ValidationErrors | null
}
los ValidationErrors
debe ser un objeto que tenga uno o más pares clave-valor:
type ValidationErrors = {
[key: string]: any;
};
La clave debe ser una cadena y se usa para denotar el tipo de error de validación como invalidEmail
, required
, etc. El valor puede ser cualquier cosa y se utiliza para proporcionar más información sobre el error de validación.
Para el ejemplo anterior, queremos escribir una función de validación personalizada que valide si no hay espacios en el nombre de usuario.
Si bien técnicamente podemos escribir esta función en cualquier lugar de la aplicación, siempre es una buena práctica poner todas las funciones de validación relacionadas dentro de una clase separada:
import { ValidationErrors, AbstractControl } from '@angular/forms';
export class UserRegistrationFormValidators {
static usernameShouldBeValid(control: AbstractControl): ValidationErrors | null {
if ((control.value as string).indexOf(' ') >= 0) {
return { shouldNotHaveSpaces: true }
}
// If there is no validation failure, return null
return null;
}
}
Nota: En este ejemplo, hemos devuelto true
como el valor de la clave shouldNotHaveSpaces
porque no necesitamos proporcionar ningún detalle. En algunos casos, es posible que deba proporcionar detalles, por ejemplo:
return { maxlengthExceeded: {
maxLength: 20,
actual: control.value.length
}
}
A continuación, podemos usar esta función de validación UserRegistrationFormValidators.usernameShouldBeValid
Para el username
control de formulario en nuestro formulario basado en plantillas:
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username"
required
UserRegistrationFormValidators.usernameShouldBeValid
[(ngModel)]="person.username" name="username">
</div>
Formas reactivas
En formas reactivas, creamos FormControl
objetos explícitamente en el componente de esa plantilla. Aquí está el formulario HTML normal sin ningún ngModel
directiva o validaciones:
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name">
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username">
</div>
Supongamos que queremos convertir nuestro formulario basado en plantillas del ejemplo anterior en un formulario reactivo.
Para esto, primero, necesitamos crear explícitamente FormGroup
y FormControls
para cada campo en el componente de la plantilla:
form = new FormGroup({
'name': new FormControl(),
'username': new FormControl(),
})
Nota: Como se mencionó anteriormente, un formulario puede tener más de una FormGroup
. En este caso, podemos tener una estructura anidada:
registrationForm = new FormGroup({
'personalDetailsForm': new FormGroup({
'name': new FormControl()
})
})
Puedes leer más sobre FormGroup
en el Documentación angular.
Permítanme devolver su atención a nuestro caso de uso.
A continuación, debemos asociar estos FormControl
objetos a los campos en el formulario HTML.
<form [formGroup]="registrationForm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name"
[formControlName]="name">
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username"
[formControlName]="username">
</div>
<form>
Aquí aplicamos el formGroup
directiva y la asoció con la FormGroup
objeto registrationForm
que creamos en el componente. También asociamos el formControlName
directiva con el respectivo FormControl
objetos name
y username
.
Nota: Las directivas para construir formas reactivas se definen en ReactiveFormsModule
. Entonces, si recibe un error como:
Can't bind to formGroup
… entonces debe verificar si ha importado eso ReactiveFormsModule
en tu módulo principal app.module.ts
.
Validaciones en formas reactivas
En formas reactivas, no pasamos la ngModel
directiva y tampoco utilizamos atributos de validación HTML5. Especificamos validadores al crear los objetos del FormControl
en el propio componente.
Aquí está la firma del FormControl
clase:
class FormControl extends AbstractControl {
constructor(formState: any = null, validatorOrOpts?: ValidatorFn | AbstractControlOptions | ValidatorFn[], asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[])
// ...
}
Como podemos ver, el primer parámetro es el estado inicial del control que se puede mantener vacío, es decir ''
. El segundo parámetro es ValidatorFn
.
Para agregar las funciones de validador integradas para un FormControl
podemos pasarlo el apropiado ValidatorFn
. Para el siguiente ejemplo, hemos utilizado los siguientes validadores integrados required
, minLength
y maxLength
-:
registrationForm = new FormGroup({
'name': new FormControl('Enter your name', [
Validators.required,
Validators.minLength(2),
Validators.maxLength(30)
]),
'username': new FormControl('', Validators.required),
})
Nota: Necesitaría importar Validators
en el componente.
Tenga en cuenta también que, a diferencia de los formularios basados en plantillas, no utilizamos los atributos de validación. Usamos los respectivos ValidatorFn
me gusta Validators.required
, Validators.minLength (2), etc. Su editor de código puede proporcionar autocompletar para todos ValidatorFn
en el momento en que escribes Validators
seguido de un punto .
.
Podemos volver a la plantilla y escribir mensajes de validación:
<form [formGroup]="registrationForm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name"
[formControlName]="name">
<div *ngIf="registrationForm.get('name').invalid && (registrationForm.get('name').dirty || registrationForm.get('name').touched)"
class="alert alert-danger">
<div *ngIf="registrationForm.get('name').errors.required">
Name is required.
</div>
<div *ngIf="registrationForm.get('name').errors.minlength">
Name cannot be more than 30 characters long.
</div>
<div *ngIf="registrationForm.get('name').errors.minlength">
Name must be at least 2 characters long.
</div>
</div>
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username"
[formControlName]="username">
</div>
<form>
Validadores personalizados para formularios reactivos
Necesitamos escribir la función de validación personalizada de la misma manera que lo hicimos para la sección de formulario basado en plantillas. Podemos usar la misma función de validación personalizada UserRegistrationFormValidators.usernameShouldBeValid
en el componente de la forma reactiva:
registrationForm = new FormGroup({
'name': new FormControl('Enter your name', [
Validators.required,
Validators.minLength(2),
Validators.maxLength(30)
]),
'username': new FormControl('', [
Validators.required,
UserRegistrationFormValidators.usernameShouldBeValid
]),
})
Conclusión
En este tutorial, exploramos las dos formas diferentes de manejar las entradas del usuario: formularios basados en plantillas y reactivos. Aprendimos a validar ambos tipos de formularios. Y finalmente, también escribimos nuestra función de validación personalizada y la incluimos con los validadores integrados.
Como podemos ver, Angular tiene un gran soporte para formularios y proporciona algunas características útiles debajo del capó para validar formularios. Proporcionar cada característica con formas angulares está más allá del alcance de este tutorial. Puede leer el Documentación angular para obtener información completa.