Angular响应式表单自定义校验在控件touched时未触发如何解决
问题触发原因
Angular 响应式表单的校验函数仅会在以下场景触发执行:
- 控件的value值发生变更
- 手动调用控件的
updateValueAndValidity()方法 - 所在表单提交时
控件的touched、dirty等交互状态的变化,本身不会触发校验函数重新运行。你点击输入框触发blur时,仅更新了控件的touched状态为true,控件值没有发生变化,因此自定义校验器不会执行,内部的判断逻辑自然无法生效。
你配置updateOn: 'blur'未生效的原因是:该配置仅指定值变更后触发校验的时机,若blur时控件值和上一次校验时的取值一致,不会触发校验流程。
解决方案
方案一:将交互状态判断移到视图层(推荐)
这是Angular官方推荐的最佳实践,校验器仅负责对值的合法性做校验,和交互状态解耦,提升校验器的可复用性:
- 调整自定义校验器,移除
touched判断:
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; export class CustomValidation { static customEmailValidation(): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { if (control.value.length === 0) { console.log(control); return { customError: { hasError: true, errorType: 'empty field', errorLabel: 'test', }, }; } return null; }; } }
- 在模板展示错误时,再加入
touched状态判断:
<form [formGroup]="customForm"> <input formControlName="customInput" /> <!-- 仅在控件touched时展示错误 --> <span *ngIf="customForm.get('customInput').touched && customForm.get('customInput').hasError('customError')"> 请输入有效内容 </span> <button [disabled]="customForm.invalid">Submit</button> </form>
方案二:手动触发校验(适配特殊需求)
若确实有业务需要在校验器内基于touched状态做逻辑判断,可以在控件blur时手动触发校验:
- 给模板的输入框添加模板引用变量:
<input #customInput formControlName="customInput" />
- 组件内监听blur事件,手动调用校验方法:
import { ViewChild, ElementRef } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent implements OnInit, AfterViewInit { customForm: FormGroup; @ViewChild('customInput') inputEl: ElementRef<HTMLInputElement>; constructor(private fb: FormBuilder) {} ngOnInit() { this.customForm = this.fb.group({ customInput: [ '', [Validators.required, CustomValidation.customEmailValidation()], ], }); } ngAfterViewInit() { const control = this.customForm.get('customInput'); this.inputEl.nativeElement.addEventListener('blur', () => { // 手动触发全量校验,此时touched状态已经更新完成 control.updateValueAndValidity(); }); } }
内容的提问来源于stack exchange,提问作者brk
相关产品推荐
相关产品推荐

