Skip to content

Instantly share code, notes, and snippets.

@jhades
Last active July 11, 2023 20:12
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jhades/b179cbd8b6e28fa227b6e4e4231538df to your computer and use it in GitHub Desktop.
Save jhades/b179cbd8b6e28fa227b6e4e4231538df to your computer and use it in GitHub Desktop.
Angular Custom Form Validators: Complete Guide - https://blog.angular-university.io/angular-custom-validators/
User Login Form:
<form [formGroup]="form">
<input matInput type="email" name="email"
placeholder="Email" formControlName="email">
<input matInput type="password"
placeholder="Password" formControlName="password">
<button [disabled]="!form.valid">
Login
</button>
</form>
@Component({
selector: 'login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
form = this.fb.group({
email: ['', {
validators: [
Validators.required,
Validators.email
],
updateOn: 'blur'
}],
password: [
'',
[Validators.required, Validators.minLength(8),
createPasswordStrengthValidator()
]
]
});
constructor(private fb: FormBuilder) {}
get email() {
return this.form.controls['email'];
}
get password() {
return this.form.controls['password'];
}
}
form = this.fb.group({
password: [
<form initial value>,
[ ... array of synchronous validators ...]
]
});
form = this.fb.group({
email: ['', {
validators: [ ... array of synchronous validators ...],
asyncValidators: [ ... array of asynchronous validators ...]
updateOn: 'change' or 'blur' or 'submit'
}],
...
});
import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms';
export function createPasswordStrengthValidator(): ValidatorFn {
return (control:AbstractControl) : ValidationErrors | null => {
const value = control.value;
if (!value) {
return null;
}
const hasUpperCase = /[A-Z]+/.test(value);
const hasLowerCase = /[a-z]+/.test(value);
const hasNumeric = /[0-9]+/.test(value);
const passwordValid = hasUpperCase && hasLowerCase && hasNumeric;
return !passwordValid ? {passwordStrength:true}: null;
}
}
{
passwordStrength: {
hasUpperCase: true,
hasLowerCase: true,
hasNumeric: false
}
}
form = this.fb.group({
email: ...
password: [
'',
[
Validators.required,
Validators.minLength(8),
createPasswordStrengthValidator()
]
]
});
<form #loginForm="ngForm" (ngSubmit)="login(loginForm, $event)">
<input matInput type="email" name="email"
ngModel
#email="ngModel"
required email minlength="3" maxlength="20"
placeholder="Email">
<input matInput type="password" name="password"
required passwordStrength minlength="8"
ngModel #password="ngModel"
placeholder="Password">
</mat-form-field>
<button type="submit" [disabled]="!loginForm.valid">
Login
</button>
</form>
import {Directive} from '@angular/core';
import {AbstractControl, NG_VALIDATORS, ValidationErrors, Validator} from '@angular/forms';
import {createPasswordStrengthValidator} from '../validators/password-strength.validator';
@Directive({
selector: "[passwordStrength]",
providers: [{
provide: NG_VALIDATORS,
useExisting:PasswordStrengthDirective,
multi: true
}]
})
export class PasswordStrengthDirective implements Validator {
validate(control: AbstractControl): ValidationErrors | null {
return createPasswordStrengthValidator()(control);
}
}
<input matInput type="password"
placeholder="Password" formControlName="password">
<div class="field-message" *ngIf="password.errors?.passwordStrength">
Your password must have lower case, upper case and numeric characters.
</div>
<form [formGroup]="form">
<input placeholder="Start date" formControlName="startAt">
<input placeholder="End date" formControlName="endAt">
</form>
import {FormGroup, ValidatorFn, Validators} from '@angular/forms';
export function creatDateRangeValidator(): ValidatorFn {
return (form: FormGroup): ValidationErrors | null => {
const start:Date = form.get("startAt").value;
const end:Date = form.get("endAt").value;
if (start && end) {
const isRangeValid = (end.getTime() - start.getTime() > 0);
return isRangeValid ? null : {dateRange:true};
}
return null;
}
}
form = this.fb.group({
startAt: [null, Validators.required],
endAt: [null, Validators.required]
}, {
validators: [createDateRangeValidator()]
});
Create User Form:
<form [formGroup]="form">
<input matInput type="email" name="email"
placeholder="Enter the new user email" formControlName="email" #email>
<div class='field-message' *ngIf="email?.errors.userExists">
An user with email {{email.value}} already exists.
</div>
<input matInput type="password"
placeholder="Password" formControlName="password">
<input matInput type="password"
placeholder="Confirm Password" formControlName="confirmPassword">
<button [disabled]="!form.valid">
Create User
</button>
</form>
@Component({
selector: 'login',
templateUrl: './create-user.component.html',
styleUrls: ['./create-user.component.css']
})
export class CreateUserComponent {
form = this.fb.group({
email: ['', {
validators: [
Validators.required,
Validators.email
],
asyncValidators: [userExistsValidator(this.user)]
updateOn: 'blur'
}],
....
});
constructor(private fb: FormBuilder, private user: UserService) {}
get email() {
return this.form.controls['email'];
}
}
import {AbstractControl, AsyncValidatorFn} from '@angular/forms';
export function userExistsValidator(user: UserService):AsyncValidatorFn {
return (control: AbstractControl) => {
return user.findUserByEmail(control.value)
.pipe(
map(user => user ? {userExists:true} : null)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment