crearemos un formulario de registro simple, con correo electrónico, contraseña y controles de confirmación de contraseña. Luego validaremos los datos para garantizar que cumplan con nuestros requisitos antes de que un usuario pueda enviar el formulario. Vamos a aplicar las siguientes reglas para la contraseña:
- Debe tener al menos 8 caracteres de longitud,
- Debe ser alfanumérico, con al menos un carácter en mayúscula y uno en minúscula
- Y debe tener al menos un caracter especial.
Vamos a validar cada una de las 5 reglas individualmente usando RegExp. Y luego, mostraremos una agradable interfaz visual que le indicará al usuario qué regla no ha cumplido. También verificaremos si la contraseña y las contraseñas de confirmación coinciden.
Vamos a utilizar bootstrap para la interfaz de usuario y los iconos de material establecidos para los iconos. Siéntase libre de usar la biblioteca de UI y el conjunto de iconos de su elección.
Empezando
Primero, vamos a crear un nuevo proyecto usando CLI angular.
ng nuevo ng-bootstrap-password-validation-example
Luego, debemos instalar y configurar bootstrap para nuestro proyecto angular. Primero, instale bootstrap usando su administrador de paquetes favorito:
npm install -s bootstrap
// o
yarn add bootstrap
Luego, agregue estilos de arranque SCSS a la lista de estilos para su proyecto dentro de su angular.json
archivo. Hay tres de ellos, pero solo uno es necesario node_modules/bootstrap/scss/bootstrap.scss
.
...
"styles": [
"src/styles.css",
"node_modules/bootstrap/scss/bootstrap-grid.scss",
"node_modules/bootstrap/scss/bootstrap-reboot.scss",
"node_modules/bootstrap/scss/bootstrap.scss"
],
...
Luego, necesitamos agregar íconos de material a nuestro proyecto angular. Abra su index.html (ubicado dentro del src
directorio en la raíz de su espacio de trabajo angular) y agregue el siguiente enlace:
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
Esta es la forma más fácil de agregar íconos de material a su proyecto. Si está interesado en conocer las otras formas de agregar íconos de materiales a cualquier proyecto, puede visitar la guía oficial aquí . Y finalmente, necesitamos realinear verticalmente los íconos de materiales, para que parezcan más centralizados. Utilizaremos el siguiente código CSS:
.material-icons {
display: inline-flex;
vertical-align: middle;
}
Agregue el código anterior en los estilos de su aplicación, por defecto está styles.css
ubicado debajo del src
directorio en la raíz de su espacio de trabajo angular. Lo último que debemos hacer es importar los módulos que necesitamos en nuestro módulo de aplicación. En este proyecto, solo vamos a requerir ReactiveFormsModule
, ya que estamos construyendo una forma reactiva. Entonces, agreguemos ese módulo a nuestras importaciones en el módulo de la aplicación (Por defecto app.module.ts
).
@NgModule({
...
imports: [
BrowserModule,
ReactiveFormsModule
],
...
})
export class AppModule {}
Crear un validador personalizado
Angular proporciona validadores integrados, pero en nuestro caso no nos ayudarán a lograr lo que queremos. Necesitamos un validador personalizado que use expresiones regulares para verificar si una contraseña contiene un carácter especial o un número e informarnos el error.
Por lo tanto, nuestro validador personalizado aceptará la expresión RegExp de validación y el objeto de error de validación para devolver si encuentra un error. Por ejemplo, si queremos verificar si tiene un número, pasaremos lo siguiente:
patternValidator(/\d/, { hasNumber: true }),
¿Dónde /\d/
está la expresión RegExp para verificar si tiene un número y {hasNumber: true} es nuestro error para devolver? La parte de error de nuestros parámetros nos permitirá usar el control
. hasError("hasNumber")
dentro de nuestra plantilla, al verificar si se muestra el error. Ahora, construyamos nuestro validador. Primero, vamos a crear una CustomValidators
clase, donde podemos colocar múltiples métodos de validación personalizados para nuestro proyecto.
validadores personalizados de clase $ ng g
Luego, vamos a agregar un método de validación de patrón estático (patternValidator).
static patternValidator(regex: RegExp, error: ValidationErrors): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
if (!control.value) {
// if control is empty return no error
return null;
}
// test the value of the control against the regexp supplied
const valid = regex.test(control.value);
// if true, return no error (no error), else return error passed in the second parameter
return valid ? null : error;
};
}
Y también necesitamos un segundo CustomValidator para verificar si nuestra contraseña y confirmar la contraseña coinciden. Agregue un segundo CustomValidator, llamado passwordMatchValidator
y agregue el siguiente código.
static passwordMatchValidator(control: AbstractControl) {
const password: string = control.get('password').value; // get password from our password form control
const confirmPassword: string = control.get('confirmPassword').value; // get password from our confirmPassword form control
// compare is the password math
if (password !== confirmPassword) {
// if they don't match, set an error in our confirmPassword form control
control.get('confirmPassword').setErrors({ NoPassswordMatch: true });
}
}
Construyendo nuestro formulario de registro
Para este proyecto, vamos a utilizar el componente predeterminado – AppComponent
.
Clase de componente
Primero, necesitamos inyectar el FormBuilder en nuestro componente.
constructor(private fb: FormBuilder) {}
A continuación, debemos agregar una nueva propiedad en nuestra clase de componente frmSignup
, que contendrá información sobre nuestro formulario:
public frmSignup: FormGroup;
A continuación, debemos crear un método para agregar controles de formulario a la frmSignup
propiedad que acabamos de crear: llamamos al método createSignupForm y devolverá una clase FormGroup.
createSignupForm(): FormGroup {}
Luego, dentro del método createSignupForm () anterior, necesitamos agregar los controles para nuestro formulario y las reglas de validación requeridas.
createSignupForm(): FormGroup {
return this.fb.group(
{
// email is required and must be a valid email email
email: [null, Validators.compose([
Validators.email,
Validators.required])
],
password: [ null, Validators.compose([
// 1. Password Field is Required
Validators.required,
// 2. check whether the entered password has a number
CustomValidators.patternValidator(/\d/, { hasNumber: true }),
// 3. check whether the entered password has upper case letter
CustomValidators.patternValidator(/[A-Z]/, { hasCapitalCase: true }),
// 4. check whether the entered password has a lower-case letter
CustomValidators.patternValidator(/[a-z]/, { hasSmallCase: true }),
// 5. check whether the entered password has a special character
CustomValidators.patternValidator(/[ [!@#$%^&*()_+-=[]{};':"|,.<>/?]/](<mailto:!@#$%^&*()_+-=[]{};':"|,.<>/?]/>), { hasSpecialCharacters: true }),
// 6. Has a minimum length of 8 characters
Validators.minLength(8)])
],
confirmPassword: [null, Validators.compose([Validators.required])]
},
{
// check whether our password and confirm password match
validator: CustomValidators.passwordMatchValidator
});
}
Ahora pasemos a nuestra plantilla de componentes.
Plantilla de componente:
Crearemos una forma reactiva normal como lo haría normalmente:
<form [formGroup]="frmSignup" (submit)="submit()"></form>
Luego, agregamos nuestros controles de formulario individuales:
<input id="email" formControlName="email" type="email" class="form-control" />
Luego, debemos mostrar los errores de validación a los usuarios. Por ejemplo, para verificar si el correo electrónico proporcionado es válido, utilizamos la siguiente expresión:
frmSignup.controls ['email']. hasError ('email')
Y lo mismo para un campo de formulario requerido:
frmSignup.controls ['email']. hasError ('obligatorio')
Y para mostrar un mensaje, simplemente podemos usar *ngIf
:
<label
class="text-danger"
*ngIf="frmSignup.controls['email'].hasError('email')"
>
Enter a valid email address!
</label>
Esto mostrará el Enter a valid Email Address!error solo cuando haya tal error. Puede hacer lo mismo para los errores de validación requeridos y personalizados.
Lo mismo se aplica para confirmar si la contraseña de confirmación y la contraseña coinciden:
<label
class="text-danger"
*ngIf="frmSignup.controls['confirmPassword'].hasError('NoPassswordMatch')"
>
Password do not match
</label>
También podemos agregar un borde rojo alrededor de nuestro campo de formulario para darle importancia al error. Vamos a agregar la is-invalid
clase bootstrap usando ngClass, agregando la clase cuando el control de formulario tiene un error y la eliminamos cuando no hay error:
[ngClass]="frmSignup.controls['email'].invalid ? 'is-invalid' : ''"
Entonces, nuestro campo de formulario se ve así:
<input
id="email"
formControlName="email"
type="email"
class="form-control"
[ngClass]="frmSignup.controls['email'].invalid ? 'is-invalid' : ''"
/>
Validación de reglas de contraseña
No hay mucha diferencia con el código anterior cuando se ajustan a las reglas de validación personalizadas.
<label
class="text-danger"
*ngIf="frmSignup.controls['password'].hasError('hasNumber')"
>
Must have at least 1 number!
</label>
Pero, dado que no solo queremos ocultar y mostrar los errores como con otros campos de formulario, queremos mostrar texto con fuente verde para las reglas que la contraseña ha cumplido, y un color de fuente rojo para las reglas no cumplidas. Entonces, en lugar de *ngIf
, usaremos un ngClass, para cambiar entre clases bootstrap text-success
y text-danger
.
[ngClass]="frmSignup.controls['password'].hasError('required') ||
frmSignup.controls['password'].hasError('hasNumber') ? 'text-danger' :
'text-success'"
También debemos hacer lo mismo con los íconos, mostrar un ícono de verificación cuando se haya cumplido una regla y cancelar el ícono de lo contrario.
<i class="material-icons">
{{ frmSignup.controls['password'].hasError('required') ||
frmSignup.controls['password'].hasError('hasNumber') ? 'cancel' :
'check_circle' }}
</i>
Entonces, nuestro código completo se verá así para verificar si la contraseña tiene un número:
<label
class="col"
[ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasNumber') ? 'text-danger' :'text-success'"
>
<i class="material-icons">
{{
frmSignup.controls['password'].hasError('frmSignup.controls['password'].hasError('hasNumber')
? 'cancel' : 'check_circle' }}
</i>
Must contain atleast 1 number!
</label>
Y lo mismo ocurre con nuestras otras reglas de contraseña:
<label
[ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('minlength') ? 'text-danger' : 'text-success'"
>
<i class="material-icons">
{{ frmSignup.controls['password'].hasError('required') ||
frmSignup.controls['password'].hasError('minlength') ? 'cancel' :
'check_circle' }}
</i>
Must be at least 8 characters!
</label>
<label
class="col"
[ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasNumber') ? 'text-danger' : 'text-success'"
>
<i class="material-icons">
{{ frmSignup.controls['password'].hasError('required') ||
frmSignup.controls['password'].hasError('hasNumber') ? 'cancel' :
'check_circle' }}
</i>
Must contain at least 1 number!
</label>
<label
class="col"
[ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasCapitalCase') ? 'text-danger' : 'text-success'"
>
<i class="material-icons">
{{ frmSignup.controls['password'].hasError('required') ||
frmSignup.controls['password'].hasError('hasCapitalCase') ? 'cancel' :
'check_circle' }}
</i>
Must contain at least 1 in Capital Case!
</label>
<label
class="col"
[ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasSmallCase') ? 'text-danger' : 'text-success'"
>
<i class="material-icons">
{{ frmSignup.controls['password'].hasError('required') ||
frmSignup.controls['password'].hasError('hasSmallCase') ? 'cancel' :
'check_circle' }}
</i>
Must contain at least 1 Letter in Small Case!
</label>
<label
class="col"
[ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasSpecialCharacters') ? 'text-danger' : 'text-success'"
>
<i class="material-icons">
{{ frmSignup.controls['password'].hasError('required') ||
frmSignup.controls['password'].hasError('hasSpecialCharacters') ? 'cancel' :
'check_circle' }}
</i>
Must contain at least 1 Special Character!
</label>