【问题标题】:Synchronize the state of validation for custom control with inner PrimeNG control in Angular将自定义控件的验证状态与 Angular 中的内部 PrimeNG 控件同步
【发布时间】:2021-08-19 16:26:26
【问题描述】:

我正在尝试围绕 PrimeNG 日历控件创建一个包装器对象,并且在多年未使用 Angular 进行开发之后,我仍然需要注意,扩展控件及其所有控件功能没有简单的方法。

我想要的是,将必要的功能(例如设置验证状态,例如“ng-pristine”或“ng-dirty”)传递给内部控件。但这不能正常工作。也许有人已经解决了这个问题!

const noop = () => {
};

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => CalendarFieldComponent),
  multi: true
};

@Component({
  selector: 'calendar-field',
  templateUrl: './calendar-field.component.html',
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR],

})
export class CalendarFieldComponent implements ControlValueAccessor, Validator {

  @Input() required = false;
  @ViewChild(NgModel) model?: NgModel;

  private innerValue: any = '';

  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (_: any) => void = noop;

  private onValidatorChange: (_: any) => void = noop;

  get value(): any {
    return this.innerValue;
  };

  set value(v: any) {
    console.log(this.model);
    this.model?.control.markAsDirty();
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  onBlur() {
    this.onTouchedCallback();
  }

  writeValue(value: any) {
    if (value !== this.innerValue) {
      this.innerValue = value;
    }
  }

  registerOnChange(fn: any) {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  }

  validate(control: AbstractControl): ValidationErrors | null {
    return {'required': true};
  }

  registerOnValidatorChange(fn: () => void): void {
    this.onValidatorChange = fn;
  }
}

模板如下所示:

<p-calendar [(ngModel)]="value" styleClass="inputfield w-full" appendTo="body" [required]="required" (blur)="onBlur()"></p-calendar>

当我有一个包含上述控件的 FormGroup 时。提交表单后,我想验证所有字段,该字段已经使用以下代码工作。所有空字段都有一个红色边框(因为 ng-invalid),除了被包装的控件。

// code which validates all the fields when clicking on submit button
private validateForm(): boolean {
    for (const controlKey of Object.keys(this.form.controls)) {
        const control = this.form.controls[controlKey];
        control.markAllAsTouched();
        control.markAsDirty();
        control.markAsTouched();
    }
    return !!this.form.valid;
}

包装控件获取类“ng-invalid ng-touched ng-dirty”,而内部控件只获取类“ng-untouched ng-pristine ng-invalid ng-star-inserted”。

我怎样才能同步这个(从包装器到内部)?

【问题讨论】:

    标签: angular typescript validation primeng


    【解决方案1】:

    我正在做这样的事情来将内部控件的错误与外部控件同步

    validate(control: AbstractControl): ValidationErrors | null {
        return {'required': this.validateForm()};
    }
    
    

    为了实现另一种方式(在内部控件上设置错误)我做了这样的事情:

    const ngControl = this.injector.get(NgControl).control;
    ngControl.statusChanges.pipe(filter(x => x === 'INVALID' || x === 'VALID' )).subscribe(_ => this.setInputErrors())
    
    setInputErrors(): void {
        const ngControl = this.injector.get(NgControl).control;
        if (ngControl.invalid) {
            this.innerFormControl.setErrors( { incorrect: true });
        else {
            this.innerFormControl.setErrors(null);
        }
    }
    

    对于这个解决方案,我使用的是响应式表单方法,而不是模板驱动。这对我来说在大多数情况下都有效,但您可能会进行一些更改。

    【讨论】:

    • 我希望有一种更有效的方法来检索 NgControl。至少在模板驱动的表单中,获得控件确实很困难,而且在我的情况下,设置或检索 PrimeNG 组件的控件也是不可能的。看来,我不得不接受这样一个事实,即“输入树”无法同步。但是感谢您的方法。
    • 在这种情况下切换到反应式有什么问题?
    • 这太费力了(我在应用程序中有多个大型表单)而且我没有感觉,一切都会在之后工作。也许我仍然无法获得 FormControl。目前我最终可以从 ControlContainer 和 Input('name') 获得 FormControl。但是当 ngOnInit 被触发时 formGroup 不可用。也不在 AfterViewInit 中。不确定,如果反应式表单是另一种情况。
    猜你喜欢
    • 2011-09-29
    • 2018-12-25
    • 2011-02-21
    • 2020-04-06
    • 2023-03-21
    • 2018-07-17
    • 2018-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多