在Angular中使用ControlValueAccessor继承验证

mat*_*l74 12 forms validation controls angular

我有一个自定义表单控件组件(它是一个美化的输入).它是一个自定义组件的原因是为了便于UI更改 - 即如果我们改变我们对输入控件进行样式设置的方式,则可以很容易地在整个应用程序中传播更改.

目前我们在Angular https://material.angular.io中使用Material Design

哪些样式在无效时控制得非常好.

我们已经实现了ControlValueAccessor,以便允许我们将formControlName传递给我们的自定义组件,该组件工作正常; 当自定义控件有效/无效且应用程序按预期运行时,表单有效/无效.

但是,问题是我们需要根据自定义组件是否无效来设置自定义组件的样式,我们似乎无法做到这一点 - 实际需要设置样式的输入永远不会被验证,它只需将数据传入父组件或从父组件传递数据.

COMPONENT.ts

import { Component, forwardRef, Input, OnInit } from '@angular/core';
import {
    AbstractControl,
    ControlValueAccessor,
    NG_VALIDATORS,
    NG_VALUE_ACCESSOR,
    ValidationErrors,
    Validator,
} from '@angular/forms';

@Component({
  selector: 'app-input',
  templateUrl: './input.component.html',
  styleUrls: ['./input.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputComponent),
      multi: true
    }
  ]
})
export class InputComponent implements OnInit, ControlValueAccessor {
  writeValue(obj: any): void {
    this._value = obj;
  }
  registerOnChange(fn: any): void {
    this.onChanged = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  get value() {
    return this._value;
  }

  set value(value: any) {
    if (this._value !== value) {
      this._value = value;
      this.onChanged(value);
    }
  }

  @Input() type: string;

  onBlur() {
    this.onTouched();
  }

  private onTouched = () => {};
  private onChanged = (_: any) => {};
  disabled: boolean;

  private _value: any;

  constructor() { }

  ngOnInit() {
  }

}
Run Code Online (Sandbox Code Playgroud)

COMPONENT.html

<ng-container [ngSwitch]="type">
  <md-input-container class="full-width" *ngSwitchCase="'text'">
    <span mdPrefix><md-icon>lock_outline</md-icon> &nbsp; </span>
    <input mdInput placeholder="Password" type="text" [(ngModel)]="value" (blur)="onBlur()" />
  </md-input-container>
</ng-container>
Run Code Online (Sandbox Code Playgroud)

示例在页面上使用:

HTML:

<app-input type="text" formControlName="foo"></app-input>
Run Code Online (Sandbox Code Playgroud)

TS:

this.form = this.fb.group({
        foo: [null, Validators.required]
    });
Run Code Online (Sandbox Code Playgroud)

mat*_*l74 5

答案在这里找到:

从 Angular 中的自定义表单组件访问 FormControl

不确定这是最好的方法,我希望有人能找到更漂亮的方法,但是将子输入绑定到以这种方式获得的表单控件解决了我们的问题


VJA*_*JAI 5

您可以NgControl通过DI 访问。NgControl具有有关验证状态的所有信息。要进行检索,NgControl您不应提供组件,NG_VALUE_ACCESSOR而应在构造函数中设置访问器。

@Component({
  selector: 'custom-form-comp',
  templateUrl: '..',
  styleUrls: ...
})
export class CustomComponent implements ControlValueAccessor {

   constructor(@Self() @Optional() private control: NgControl) {
     this.control.valueAccessor = this;
   }

   // ControlValueAccessor methods and others

   public get invalid(): boolean {
     return this.control ? this.control.invalid : false;
   }

   public get showError(): boolean {
      if (!this.control) {
       return false;
      }

      const { dirty, touched } = this.control;

      return this.invalid ? (dirty || touched) : false;
   }
}
Run Code Online (Sandbox Code Playgroud)

请阅读本文以了解完整的信息。

  • 对于 Angular 8,我可以将构造函数简化为 `constructor(private control: NgControl) { this.control.valueAccessor = this; }` (3认同)
  • 所以我们通过注入的 NgControl 检索到 formControlName="foo" 是否有效的事实,对吗?我们可以在 custom-form-comp 的模板中使用 get invalid() 布尔变量来设置其内部元素的样式,对吗?有没有办法(并且有意义)将类 ng-touched、ng-dirty、ng-invalid 等从 &lt;custom-form-comp&gt; 组件转发到其内部元素? (2认同)
  • 这非常有帮助,尽管我必须重新阅读几次才能注意到我需要“停止”通过“NG_VALUE_ACCESSOR”提供并使用“.valueAccessor”来代替。谢谢! (2认同)
  • @Coderer,感谢您的评论,我能够使其工作,我收到循环依赖错误..没有注意到答案中提供者数组已被省略..很好! (2认同)