export const FieldMatchValidator = (errorName: string = 'notMatched') => { return (...formControlNames: string[]): ValidatorFn => { return (group: FormGroup): { [key: string]: any } => { const controls: AbstractControl[] = formControlNames.map(name => { return group.controls[name]; }); const areMatched = controls.every((control, index, array) => { if (!control) { throw new Error('Control not exist or given bad name!'); } return array[0].value === control.value; }); if (!areMatched) { controls.forEach(control => { const controlErrors = control.errors || {}; controlErrors[errorName] = true; control.setErrors(controlErrors); control.markAsTouched(); }); return { [errorName]: true }; } controls.forEach(control => { const controlErrors = control.errors || {}; delete controlErrors[errorName]; if (Object.keys(controlErrors).length === 0) { control.setErrors(null); } }); return null; }; }; }; // in component (.ts) this.form = this.formBuilder.group({ usernameInput: ['', Validators.required], emailInput: ['', [Validators.required, Validators.email]], phoneInput: ['', Validators.required], confirmPhoneInput: ['', Validators.required], passwordInput: ['', [Validators.required]], confirmPasswordInput: ['', [Validators.required]] }, { validators: [ FieldMatchValidator('notMatchedPasswords')('passwordInput', 'confirmPasswordInput'), FieldMatchValidator('notMatchedPhones')('phoneInput', 'confirmPhoneInput') ] }); // in component (.html) *ngIf="form.hasError('notMatchedPasswords')" or *ngIf="form.controls['passwordInput'].hasError('notMatchedPasswords')"