Angular自定义Validator如何在其他关联字段值变化时重新触发校验
解决方案1:修改自定义指令,内置监听关联控件变化(推荐)
你原来的代码里空实现了registerOnValidatorChange接口,这个接口正好用来注册校验触发回调,我们可以监听关联的密码控件值变化,主动触发当前控件的重新校验。这个方案不需要修改任何模板代码,原有逻辑完全兼容。
修改后的指令代码如下:
import { Directive, Input, OnInit, OnDestroy } from '@angular/core'; import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; import { Subscription } from 'rxjs'; @Directive({ selector: '[appMatchPassword]', providers: [ { provide: NG_VALIDATORS, useExisting: MatchPasswordDirective, multi: true } ] }) export class MatchPasswordDirective implements Validator, OnInit, OnDestroy { @Input('appMatchPassword') otherControl: string = ''; private onChange?: () => void; private valueChangeSub?: Subscription; private control?: AbstractControl; constructor() { } ngOnInit(): void { // 等待表单控件初始化完成后再获取关联控件 setTimeout(() => { const compareTo = this.control?.parent?.get(this.otherControl); if (compareTo) { // 关联控件值变化时,触发当前控件重新校验 this.valueChangeSub = compareTo.valueChanges.subscribe(() => { this.onChange?.(); }); } }) } registerOnValidatorChange(fn: () => void): void { this.onChange = fn; } validate(control: AbstractControl): ValidationErrors | null { this.control = control; const compareTo = control.parent?.get(this.otherControl); if (!compareTo) return null; return compareTo.value !== control.value ? { appMatchPassword: true } : null; } ngOnDestroy(): void { // 取消订阅避免内存泄漏 this.valueChangeSub?.unsubscribe(); } }
解决方案2:模板手动触发校验(无需修改指令,快速实现)
直接在密码输入框的ngModelChange事件里手动触发确认密码控件的重新校验,仅修改模板代码即可完成:
<mat-form-field> <mat-label>Password</mat-label> <input matInput type="password" name="password" [(ngModel)]="user.password" required minlength="4" #iPassword="ngModel" (ngModelChange)="fvp.control.updateValueAndValidity()"> <mat-error *ngIf="iPassword.errors?.required">Password Required.</mat-error> <mat-error *ngIf="iPassword.errors?.minlength">Minimum 4 characters</mat-error> </mat-form-field> <mat-form-field> <mat-label>Verify Password</mat-label> <input matInput type="password" name="vpassword" [(ngModel)]="vpassword" required appMatchPassword="password" #fvp="ngModel"> <mat-error *ngIf="fvp.errors?.appMatchPassword">Passwords do not match</mat-error> </mat-form-field>
解决方案3:将验证器放到表单组层面
如果你用响应式表单管理,可以把密码匹配验证器绑定到整个表单组上,这样表单组内任意控件值变化都会自动触发校验:
// 表单定义时添加组级别校验 this.form = this.fb.group({ password: ['', [Validators.required, Validators.minLength(4)]], vpassword: ['', Validators.required] }, { validators: (group: FormGroup) => { return group.get('password')?.value === group.get('vpassword')?.value ? null : { appMatchPassword: true } } })
模板里调整错误判断逻辑即可:
<mat-error *ngIf="form.errors?.appMatchPassword && fvp.touched">Passwords do not match</mat-error>
内容的提问来源于stack exchange,提问作者AlanObject
相关产品推荐
相关产品推荐

