Εμφάνιση αναρτήσεων με ετικέτα formControlName. Εμφάνιση όλων των αναρτήσεων
Εμφάνιση αναρτήσεων με ετικέτα formControlName. Εμφάνιση όλων των αναρτήσεων

Σάββατο 27 Ιουνίου 2020

Angular change password form with validators, error handling and password confirmation










Create a form for changing a password



Verify same password in both inputs

Apply error displays, according to case

Toggle password visibility











1. Component - HTML


<div class="password-reset"> <form role="form" (ngSubmit)="submit()" [formGroup]="changePasswordForm" autocomplete="off" > <div fxLayout="column" fxLayoutAlign="space-between" fxLayoutGap="5.7vw"> <div> <mat-form-field hideRequiredMarker class="full-width"> <mat-label>Password</mat-label> <input matInput name="password" id="password" formControlName="password" [type]="hidePassword ? 'password' : 'text'" > <mat-error *ngIf="doShowHintsForCorrectPassword()"> {{ doDisplayHintsForCorrectPassword() }} </mat-error> <button type="button" mat-icon-button matSuffix (click)="hidePassword = !hidePassword" > <mat-icon>{{ hidePassword ? 'visibility_off' : 'visibility' }}</mat-icon> </button> </mat-form-field> </div> <div> <mat-form-field hideRequiredMarker class="full-width"> <mat-label>Confirm Password</mat-label> <input matInput name="confirmPassword" id="confirmPassword" formControlName="confirmPassword" [type]="hideConfirmPassword ? 'password' : 'text'" > <mat-error *ngIf="changePasswordForm.get('confirmPassword')?.errors?.sameValue"> Confirm password error </mat-error> <button type="button" mat-icon-button matSuffix (click)="hideConfirmPassword = !hideConfirmPassword" > <mat-icon>{{ hideConfirmPassword ? 'visibility_off' : 'visibility' }}</mat-icon> </button> </mat-form-field> </div> <br> </div> <div fxLayoutAlign="space-between" fxLayoutGap="2.7vw"> <div fxFlex="50%"> <button type="submit" mat-raised-button class="full-width" [disabled]="!changePasswordForm.valid" > Save </button> </div> <div fxFlex="50%"> <button type="button" mat-raised-button class="full-width"> Cancel </button> </div> </div> </form> </div>



2. Component TS


import { Component, OnInit } from '@angular/core'; import { FormBuilder, Validators } from '@angular/forms'; import { register } from '../shared/util/component-mapping'; @Component({ selector: 'jhi-test-comp', templateUrl: './test-comp.component.html', styleUrls: ['./test-comp.component.scss'] }) @register export class TestCompComponent implements OnInit { public static COMPONENT_NAME = 'TestCompComponent'; changePasswordForm = this.fb.group({ password: ['', [Validators.required]], confirmPassword: ['', Validators.required] }); hidePassword = true; hideConfirmPassword = true; constructor(private fb: FormBuilder) {} ngOnInit(): void {} submit(): void { this.validateFormBeforeRequest(); if (!this.changePasswordForm.invalid) { // do something } } private validateFormBeforeRequest(): void { this.changePasswordForm.controls['password'].setValidators([ Validators.required, AppValidators.minLength(10), AppValidators.containsCapitalLetter(), AppValidators.containsNumber() ]); this.changePasswordForm.controls['password'].updateValueAndValidity(); this.changePasswordForm.controls['password'].valueChanges.subscribe(() => { this.changePasswordForm.controls['confirmPassword'].updateValueAndValidity(); }); this.changePasswordForm.controls['confirmPassword'].setValidators([ Validators.required, AppValidators.sameValue('password') ]); this.changePasswordForm.controls['confirmPassword'].updateValueAndValidity(); } doShowHintsForCorrectPassword(): boolean { const errors = this.changePasswordForm.controls['password'].errors; if (errors && (errors.minLength || errors.containsNumber || errors.containsCapitalLetter)) { return true; } return false; } doDisplayHintsForCorrectPassword(): string { const errors = this.changePasswordForm.controls['password'].errors; const error = 'Error! '; if (errors) { const hint1 = errors.containsNumber ? 'Should contain numbers' : ''; const hint2 = errors.containsCapitalLetter ? ', Should contain capital letter' : ''; const hint3 = errors.minLength ? ', Too short' : ''; return error + hint1 + hint2 + hint3; } else { return error; } } }



3. Component CSS


.full-width { width: 100%; } .password-reset { height: 100%; padding: 2.7vw; } mat-form-field.mat-form-field { font-size: 2.08vh; line-height: 2.6vh; }




4. app.validators.ts

import { ValidatorFn, AbstractControl } from '@angular/forms'; export class AppValidators { private static satisfiesPattern(value: string, pattern: string): boolean { return new RegExp(pattern).test(value); } static containsNumber(): ValidatorFn { return (control: AbstractControl): { [key: string]: any } | null => { const containsNumber = this.satisfiesPattern(control.value, '\\d'); return !containsNumber ? { containsNumber: { value: control.value } } : null; }; } static containsCapitalLetter(): ValidatorFn { return (control: AbstractControl): { [key: string]: any } | null => { const containsCapitalLetter = this.satisfiesPattern( control.value, '.*[A-Z].*' ); return !containsCapitalLetter ? { containsCapitalLetter: { value: control.value } } : null; }; } static minLength(minLength: number): ValidatorFn { return (control: AbstractControl): { [key: string]: any } | null => { const notEnoughLength = control.value.length < minLength; return notEnoughLength ? { minLength: { requiredLength: minLength, actualLength: control.value.length } } : null; }; } static sameValue(formControlName: string): ValidatorFn { return (control: AbstractControl): { [key: string]: any } | null => { const otherFormControlValue = control.parent.controls[formControlName].value; const notSame = control.value !== otherFormControlValue; return notSame ? { sameValue: { value: control.value, otherValue: otherFormControlValue } } : null; }; } }