Angular FormControl设置updateOn为blur时异步验证器获旧值问题
问题根源
核心问题在于模板中直接使用[formControl]绑定FormControl实例,而非通过formControlName关联到FormGroup。
虽然你通过myForm.get('title')获取的是FormGroup内的控件实例,但在<form>标签已绑定[formGroup]="myForm"的前提下,直接用[formControl]会绕过FormGroup的状态管理逻辑,导致控件的updateOn: 'blur'配置无法正确同步到验证触发时机。最终表现为:blur事件触发验证时,控件值的更新与验证执行不同步,异步验证器拿到的是旧值或初始空值。
另外还有一个潜在风险:你在ngOnInit中添加异步验证器时,@Input() category可能尚未完成父组件的赋值(若父组件在ngOnInit之后传递该值),不过这不是当前问题的直接诱因。
解决方案
1. 修正模板绑定方式
将输入框的[formControl]替换为formControlName="title",确保控件与FormGroup正确关联,让updateOn: 'blur'配置生效:
<form (ngSubmit)="save.focus()" [formGroup]="myForm"> <input formControlName="title" [isRequired]="true" [label]="'Title'" > <button #save (click)="save()" [disabled]="!myForm.valid" aria-label="submit" color="primary" mat-raised-button > SAVE </button> </form>
2. 优化异步验证器的添加逻辑(可选)
避免动态添加验证器可能带来的配置不一致,建议在FormGroup初始化时就配置异步验证器,同时针对@Input() category的变化更新验证器:
export class MyModalComponent implements OnInit, OnChanges { @Input() category: string; myForm: FormGroup = this._formBuilder.group({ title: ['', { validators: [Validators.pattern(/^(\w|[&-. ])*$/), Validators.maxLength(100), Validators.required], updateOn: 'blur' }] }); get titleFormControl(): FormControl { return <FormControl>this.myForm.get('title'); } constructor(private _myService: MyService, private _formBuilder: FormBuilder) {} ngOnInit(): void { this.updateTitleValidator(); } ngOnChanges(changes: SimpleChanges): void { if (changes['category']) { this.updateTitleValidator(); } } private updateTitleValidator(): void { if (this.category) { this.titleFormControl.setAsyncValidators([CustomValidators.titleNotExists(this._myService, this.category)]); this.titleFormControl.updateValueAndValidity({ onlySelf: true, emitEvent: false }); } } }
原理说明
当使用formControlName时,FormGroup会统一管理控件的状态流转和验证触发时机:只有在输入框失去焦点时,才会触发同步+异步验证,此时控件值已经完成更新,异步验证器能获取到最新的输入值发送请求。
而直接使用[formControl]绑定,会绕过FormGroup的状态管理逻辑,导致updateOn: 'blur'的配置无法正确关联到验证触发流程,最终出现验证时获取旧值的异常。
内容的提问来源于stack exchange,提问作者Kpo

