Validación de contraseña – Angular Reactive Forms

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.jsonarchivo. 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 srcdirectorio 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.cssubicado debajo del srcdirectorio 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 controlhasError("hasNumber")dentro de nuestra plantilla, al verificar si se muestra el error. Ahora, construyamos nuestro validador. Primero, vamos a crear una CustomValidatorsclase, 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 passwordMatchValidatory 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 frmSignuppropiedad 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-invalidclase 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-successtext-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>