实现 ControlValueAccessor 和 Validator 接口的自定义控件组件。
此自定义控件无法正确显示来自父组件验证器的错误。甚至父组件错误字段值也没有正确更新。
@Component({
selector: 'custom-input',
standalone: true,
imports: [ReactiveFormsModule, CommonModule],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => CustomInput),
},
{
provide: NG_VALIDATORS,
multi: true,
useExisting: forwardRef(() => CustomInput),
},
],
template: `
<article style="border: 1px solid green; padding: .5rem ">
<form [formGroup]="inputForm">
<input formControlName="telInput" placeholder="Telephone" (blur)="onFocusOut($event);">
</form>
<section class="code">
inner errors: {{ inputForm.get('telInput')?.errors | json }}
</section >
</article>
`,
})
export class CustomInput implements ControlValueAccessor, Validator {
inputForm!: FormGroup;
formCallbacks = {
onChange: (anyVal: any) => {},
onTouched: () => {},
onValidatorChange: () => {},
};
writeValue(obj: any): void {
this.inputForm.setValue({ telInput: obj });
}
registerOnChange(fn: any): void {
this.formCallbacks.onChange = fn;
}
registerOnTouched(fn: any): void {
this.formCallbacks.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
isDisabled ? this.inputForm.disable() : this.inputForm.enable();
}
validate(control: AbstractControl): ValidationErrors | null {
console.log('[inner] validate', control.errors);
return control.errors;
}
registerOnValidatorChange?(fn: () => void): void {
this.formCallbacks.onValidatorChange = fn;
}
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.inputForm = this.fb.group({
telInput: [''],
});
this.inputForm.valueChanges.subscribe((val) => {
this.formCallbacks.onChange(val.telInput);
});
}
onFocusOut(event: any) {
this.formCallbacks.onTouched();
}
}
但是,当此自定义控件与反应式表单和验证器一起使用时, 错误未在组件表单和自定义输入之间正确同步。
function someValidator(ac: AbstractControl): ValidationErrors | null {
if (ac.value === null) {
return null;
}
const inputVal = ac.value;
const noErrorsOk = inputVal.length > 2;
const err = inputVal !== null && noErrorsOk ? null : { dataFormat: true };
console.log('[outer] validator err', err);
return err;
}
@Component({
selector: 'app-root',
standalone: true,
imports: [CustomInput, ReactiveFormsModule, CommonModule],
template: `
<article>
<form [formGroup]="mainForm">
<custom-input formControlName="joesNumber"></custom-input>
</form>
<section class="code">
outer error: {{ mainForm.get('joesNumber')?.errors | json }}
</section>
<section>
<p>Notes: length > 2 is valid. inner error is always null and outer error is alwasy dataFormat error. </p>
</section>
</article>
`,
})
export class App {
mainForm!: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.mainForm = this.fb.group({
joesNumber: ['', [someValidator]],
});
this.mainForm.statusChanges.subscribe((val) => {
console.log('[outer] statusChange', val);
});
}
}
游乐场
当我们用Validator创建自定义表单控件时,就是做一个函数验证器,它和我们创建formControl时传递的错误不一样。你不能返回控制。错误
validate(control: AbstractControl): ValidationErrors | null {
if (control.value=="fool")
return {innerError:"I'm can not be fool"}
return null;
}
如果您想在 formControl 无效时为您的输入提供一个类,您可以声明一个变量控件
control!:AbstractControl
并在验证函数中赋予值
validate(control: AbstractControl): ValidationErrors | null {
if (!this.control)
this.control=control
...
}
所以写一些类似的东西
<input [class.invalid]="control?.errors"/>