Angular 自定义表单控件与自定义验证器同步问题

问题描述 投票:0回答:1

实现 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);
    });
  }
}

游乐场

https://stackblitz.com/edit/stackblitz-starters-3zwvin

angular angular-reactive-forms custom-controls angular-forms angular15
1个回答
0
投票

当我们用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"/>
© www.soinside.com 2019 - 2024. All rights reserved.