Подтверждение пароля с помощью пользовательских валидаторов Angular
У меня есть поле пароля, а также поле подтверждения пароля, и все работает нормально, если вы заполняете форму сверху вниз. Однако, если я введу пароль / подтверждение пароля, а затем вернусь, чтобы изменить поле пароля, поле подтверждения останется "действительным", если я не изменю его снова, чтобы пользовательский валидатор снова "проверял совпадение паролей".
form.component.ts - форма получает свой, так сказать, "макет" из register.component.ts. Так я могу динамически создавать формы.
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormControl, AbstractControl } from '@angular/forms';
import { Router } from '@angular/router';
import { ApiService } from 'src/app/services/api.service';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
// Variable Declarations
@Input() form: {
api: string
title: string,
inputs: [{ placeholder: string, type: string, name: string, validators: []}],
link: { text: string, url: string },
button: string
};
formGroup: FormGroup;
// Variable Initialization
inputControls = {};
message = {};
// Constructor
constructor(private api: ApiService, private router: Router) { }
// On Component Init
ngOnInit() {
// Build form
this.form.inputs.forEach(input => {
if (input.name === 'confirm') {
this.inputControls[input.name] = new FormControl(null, [this.PasswordsMatch]);
} else {
this.inputControls[input.name] = new FormControl(null, input.validators);
}
});
this.formGroup = new FormGroup(this.inputControls);
}
PasswordsMatch = (control: AbstractControl): {[key: string]: boolean} | null => {
// Check if root exists first - otherwise console error ".value of undefined"
if (control.root.get('password')) {
return control.value === control.root.get('password').value ? null : {mismatch: true};
}
}
}
form.component.html
<div class="container">
<form [formGroup]="formGroup">
<span>{{ form.title }}</span>
<div *ngFor = "let input of form.inputs; let i = index">
<input [type] = "input.type"
[placeholder] = "input.placeholder"
[formControlName] = "input.name">
</div>
<button [disabled]="!formGroup.valid">{{ form.button }}</button>
<a [routerLink]="form.link.url">{{ form.link.text }}</a>
</form>
</div>
register.component.ts - я не мог понять, как передать настраиваемый валидатор для поля подтверждения пароля, поэтому я просто передал Validators.required, но он просто перезаписывается настраиваемым валидатором при создании формы.
import { Component } from '@angular/core';
import { Validators } from '@angular/forms';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss']
})
export class RegisterComponent {
// Validators (Validators.required is used to differentiate between error messages)
validators = {
name: [ Validators.required, Validators.pattern('[a-zA-Z]{2,20}') ],
email: [ Validators.required, Validators.pattern('[a-zA-Z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}')],
username: [ Validators.required, Validators.pattern('[a-zA-Z0-9.+%+-]{1,30}')],
password: [ Validators.required, Validators.pattern('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9!@#$%^&*-_=+]{10,}')],
confirm: [ Validators.required, ]
};
// Form data
form = {
api: 'register',
title: 'Are you ready?',
inputs: [
{ placeholder: 'First Name', type: 'text', name: 'name', validators: this.validators.name },
{ placeholder: 'Email Address', type: 'email', name: 'email', validators: this.validators.email },
{ placeholder: 'Mage Name', type: 'text', name: 'username', validators: this.validators.username },
{ placeholder: 'Password', type: 'password', name: 'password', validators: this.validators.password },
{ placeholder: 'Confirm Password', type: 'password', name: 'confirm', validators: this.validators.confirm },
],
link: { text: 'I\'m already a Wizard!', url: '/login' },
button: 'I\'m Ready for War'
};
constructor() {}
}
Заранее спасибо за помощь.
3 ответа
Попробуйте это решение, вам нужна библиотека ng2-validation для этого
import { CustomValidators } from 'ng2-validation';
const password = new FormControl('', [Validators.required, Validators.pattern('^(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z]).{8,}$')]);
const confirmPassword = new FormControl('', [Validators.required, CustomValidators.equalTo(password)]);
this.form = this.fb.group({
password: password,
confirmPassword: confirmPassword,
});
Просто хотел добавить возможное решение / обходной путь для тех, кто пытается создавать формы аналогичным образом.
Я обновил свой form.component.html, чтобы вызвать функцию на (keyup).
<div class="container">
<form [formGroup]="formGroup">
<span>{{ form.title }}</span>
<div *ngFor = "let input of form.inputs; let i = index">
<input [type] = "input.type"
[placeholder] = "input.placeholder"
[formControlName] = "input.name"
(keyup) = "checkPasswordsMatch(input.name)">
</div>
<button [disabled]="!formGroup.valid">{{ form.button }}</button>
<a [routerLink]="form.link.url">{{ form.link.text }}</a>
</form>
</div>
В моем form.component.ts я добавил простую функцию checkPasswordsMatch, которая вручную устанавливает ошибки для поля подтверждения пароля при изменении поля пароля, если они больше не совпадают. См. Последнюю функцию ниже:
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup, FormControl, AbstractControl } from '@angular/forms';
import { Router } from '@angular/router';
import { ApiService } from 'src/app/services/api.service';
@Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss']
})
export class FormComponent implements OnInit {
// Variable Declarations
@Input() form: {
api: string
title: string,
inputs: [{ placeholder: string, type: string, name: string, validators: []}],
link: { text: string, url: string },
button: string
};
formGroup: FormGroup;
// Variable Initialization
inputControls = {};
message = {};
// Constructor
constructor(private api: ApiService, private router: Router) { }
// On Component Init
ngOnInit() {
// Build form
this.form.inputs.forEach(input => {
if (input.name === 'confirm') {
this.inputControls[input.name] = new FormControl(null, [this.PasswordsMatch]);
} else {
this.inputControls[input.name] = new FormControl(null, input.validators);
}
});
this.formGroup = new FormGroup(this.inputControls);
}
// Check if confirm === password anytime the confirm field changes
PasswordsMatch = (control: AbstractControl): {[key: string]: boolean} | null => {
// Check if root exists first - otherwise console error ".value of undefined"
if (control.root.get('password')) {
return control.value === control.root.get('password').value ? null : {mismatch: true};
}
}
// Check if confirm === password anytime the password field changes
// "input" is the name of the input field triggering this function
checkPasswordsMatch(input) {
if (input === 'password') {
const p = this.formGroup.get('password');
const c = this.formGroup.get('confirm');
c.value === p.value ? c.setErrors(null) : c.setErrors({mismatch: true});
}
}
}
Поскольку все эти входные данные являются частью одной и той же FormGroup, форма действительна только тогда, когда действительны все входные данные. Изменение пароля после подтверждения поля подтверждения пароля вызоветcheckPasswordsMatch()
для запуска и установки ошибок вручную для поля подтверждения, если они этого не делают.
Вы должны добавить проверку в группу форм, а не в поле.
Пример:
this.userform = this.fb.group({
email: new FormControl('', [ Validators.required, Validators.email ]),
passwordGroup: new FormGroup( {
password: new FormControl('', [ Validators.required, Validators.minLength(6) ]),
passwordConfirm: new FormControl('')
},
PasswordValidation.MatchPassword
)
});
export class PasswordValidation {
static MatchPassword(AC: AbstractControl) {
const password = AC.get('password').value; // to get value in input tag
const confirmPassword = AC.get('passwordConfirm').value; // to get value in input tag
if (password !== confirmPassword) {
AC.get('passwordConfirm').setErrors({ MatchPassword: true });
} else {
return null;
}
}
}