web-dev-qa-db-ja.com

AngularでControlValueAccessorを使用して検証を継承する

カスタムフォームコントロールコンポーネントがあります(これは栄光の入力です)。カスタムコンポーネントである理由は、UIの変更を容易にするためです。つまり、基本的に入力コントロールのスタイルを変更すると、アプリケーション全体に変更を簡単に反映させることができます。

現在、Angular https://material.angular.io でマテリアルデザインを使用しています

どのスタイルが無効な場合に非常にうまく制御します。

完全に機能するカスタムコンポーネントにformControlNameを渡すことができるように、ControlValueAccessorを実装しました。カスタムコントロールが有効/無効であり、アプリケーションが期待どおりに機能する場合、フォームは有効/無効です。

ただし、問題は、カスタムコンポーネント内のUIが無効かどうかに基づいてスタイルを設定する必要があることです。これは実行できないようです。実際にスタイルを設定する必要がある入力は検証されません。親コンポーネントとの間でデータを渡すだけです。

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() {
  }

}

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>

ページでの使用例:

HTML:

<app-input type="text" formControlName="foo"></app-input>

TS:

this.form = this.fb.group({
        foo: [null, Validators.required]
    });
15
matthall74

答えはここにあります:

AngularのカスタムフォームコンポーネントからFormControlへのアクセスを取得

これが最善の方法であるかどうかはわかりませんが、誰かがよりきれいな方法を見つけるのが大好きですが、この方法で取得したフォームコントロールに子入力をバインドすると、問題が解決しました

3
matthall74

DIを通じてNgControlにアクセスできます。 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;
   }
}

完全な情報を知るには、この 記事 を参照してください。

3
VJAI

加えて:汚いと考えられるかもしれませんが、それは私にとってはトリックです:

  1. コンポーネントにValidatorインターフェースを実装させます。 2検証関数で、controlcontainerを使用して、コンポーネントの外部フォームコントロールにアクセスします。
  2. 変数を使用して、親フォームコントロール(有効/無効)のステータスを追跡します。
  3. タッチしたか確認してください。 touchedがtrueでステータスが変更された場合にのみ、フィールドで検証アクションを実行します。
1
SomeOne_1