实现ControlValueAccessor与Validator的自定义控件为何与父FormGroup不同步?
背景
我有一个实现了ControlValueAccessor和Validator的自定义邮箱控件组件(EmailControlComponent)。EmailControlComponent的validate()方法接收一个AbstractControl类型的参数。根据Angular的API,每当消费该控件的父FormGroup在邮箱控件上触发valueChanges时,validate方法就会被调用。
示例
有对应的Stackblitz演示示例。
问题
为何validate()方法的AbstractControl参数状态与父FormGroup的状态不同步?查看演示并打开控制台可以看到,无论控件实际状态如何,日志中输出的control.status始终为VALID。父FormGroup能正确显示控件状态在VALID和INVALID之间切换,但validate()参数的状态从未改变。我哪里操作有误?
参考资料
- Angular官方文档:
ControlValueAccessor - Angular官方文档:
NG_VALIDATORS
问题原因
Angular调用自定义控件的validate()方法时,传入的AbstractControl参数状态是验证流程启动前的旧状态。验证逻辑的执行顺序为:
- 控件值发生变化
- Angular调用
validate()方法获取验证错误 - 根据返回的错误更新控件的
status状态
因此你在validate()方法中读取的control.status是验证未执行前的初始状态,自然始终显示为VALID,而父FormGroup展示的是验证完成后的最终状态。
解决方法
不要依赖validate()参数的状态值,直接基于控件当前的值编写验证逻辑,返回对应的错误对象即可。示例代码如下:
validate(control: AbstractControl): ValidationErrors | null { const email = control.value; const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); return isValid ? null : { invalidEmail: '邮箱格式不正确' }; }
同时,确保自定义控件正确注册验证器,在组件装饰器中配置providers:
@Component({ selector: 'app-email-control', templateUrl: './email-control.component.html', styleUrls: ['./email-control.component.css'], providers: [ { provide: NG_VALIDATORS, useExisting: forwardRef(() => EmailControlComponent), multi: true } ] })
这样父FormGroup就能根据返回的错误正确更新控件状态,无需在validate()方法中依赖传入控件的状态。
内容的提问来源于stack exchange,提问作者Joe

