web-dev-qa-db-ja.com

ネストされたコンポーネントを親が追跡し、Angularで親から値を取得できるようにするにはどうすればよいですか?

私は一連のフォームを持っています(それぞれが1つのコンポーネントによって管理されています)。

これらのフォームには入力のパターンがあり(たとえば、それらの多くはアドレスの入力を許可する必要があります)、複数のフォームで使用され、ロジックも複製したくないため、再利用可能なコンポーネントにリファクタリングする必要がありますまた、それらのテンプレート。

再利用可能な各コンポーネントは、

  1. その論理を持っている
  2. テンプレートには入力タグが含まれ、<form>タグは含まれていません
  3. クライアント検証の制約がある
  4. 親から初期値を受け取る可能性があります
  5. そのフィールドの値をオブジェクトとして親に返すことができます(例:address: {street: "...", "city": "...", ...}
  6. 検証制約が満たされていない場合、親フォームを無効にします
  7. ユーザーが値を変更したら、親フォームを「タッチ」します

このチュートリアル からAngular2について、目的を達成する方法を理解しています124

チュートリアルのソリューションでは、他の目的も達成できますが、それは親からすべてを実行するapp.component.ts#initAddressを参照)によって実現します。

どうすれば達成できますか、567、子内でコントロールとその制約を宣言しますか?

3
Cec

このような実装は使用しないでください。 ControlValueAccessor を使用する方がはるかにクリーンです。

ControlValueAccessorは、angularフォームモジュール(クラシックまたはリアクティブ)が値または状態を書き込み、コールバックを登録して変更とイベントを取得できるようにするインターフェイスです。

writeValue(value: Address): void { } // Allows angular to set a default value to the component (used by FormControl or ngModel)

registerOnChange(fn: (_: any) => void): void {} // Callback to be called when the component value change.

registerOnTouched(fn: (_: any) => void): void { } // Callback to be called when a "touch" event occurs on the component

setDisabledState(isDisabled: boolean): void { } // Allows angular to update the component disable state.

ただし、 NG_VALUE_ACCESSOR も指定する必要があります。

私はアドレスコンポーネントのためにこの速くて汚い例を書きました:

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Address } from './address';

@Component({
  selector: 'app-address-input',
  template: `
    <label for="number">Num: </label>
    <input type="number" [disabled]="disabled" name="number" id="number" (change)="numberUpdate($event)" value="{{value.num}}"/><br />
   <label for="street">Street: </label>
   <input type="text" [disabled]="disabled" (change)="streetUpdate($event)"name="street" id="street" value="{{value.street}}" /><br />
   <label for="city">City: </label>
   <input type="text" [disabled]="disabled" name="city" id="city" value="{{value.city}}" (change)="cityUpdate($event)" /><br />
   <label for="zipCode">Zip Code: </label>
   <input type="text" [disabled]="disabled" name="zipCode" id="zipCode" value="{{value.zipCode}}" (change)="zipCodeUpdate($event)" /><br />
  <label for="state">State: </label>
  <input type="text" [disabled]="disabled" name="state" id="state" value="{{value.state}}" (change)="stateUpdate($event)" /><br />
  <label for="country">Country: </label>
  <input type="text" [disabled]="disabled" name="country" id="country" value="{{value.country}}" (change)="countryUpdate($event)" />`,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => AddressInputComponent) // forward the reference,
    multi: true // allow multiple component in the same form
    }]
})
export class AddressInputComponent implements ControlValueAccessor {

  private _onChange = (_: any) => {};
  private _onTouched = (_: any) => {};
  disabled = false;

  private _value: Address = {num: undefined, street: undefined, city: undefined, state: undefined, zipCode: undefined, country: undefined}; // current value (Address is just an interface)

  set value(value: Address) { // interceptor for updating current value
    this._value = value;
   this._onChange(this._value);
  }

  get value() {
    return this._value;
  }

  writeValue(value: Address): void {
    if (value && value !== null) {
      this._value = value;
    }
  }

  registerOnChange(fn: (_: any) => void): void {
    this._onChange = fn;
  }

  registerOnTouched(fn: (_: any) => void): void {
    this._onTouched = fn; // didn't used it but you should for touch screen enabled devices.
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  numberUpdate(event: any) {
    // additional check or process
    this._updateValue(event.target.value, 'num')
  }

  streetUpdate(event: any) {
    // additional check or process
    this._updateValue(event.target.value, 'street')
  }

  cityUpdate(event: any) {
    // additional check or process
    this._updateValue(event.target.value, 'city')
  }

  zipCodeUpdate(event: any) {
    // additional check or process
    this._updateValue(event.target.value, 'zipCode')
  }

  stateUpdate(event: any) {
    // additional check or process
    this._updateValue(event.target.value, 'state')
  }

  countryUpdate(event: any) {
    // additional check or process
    this._updateValue(event.target.value, 'country');
  }

  private _updateValue(value: any, field: string) {
    const newValue = this._value;
    newValue[field] = value;
    this.value = newValue;
  }
}

次に、フォームで他のフォーム要素と同じように使用します。

<form [formGroup]="registerForm">
  <app-address-input formControlName="address"></app-address-input>
</form>

コンポーネントにロジックを追加できます。これが実際の例です。これは簡単な例であり、コードをよりクリーンにするためにやり直す必要があることに注意してください。

https://stackblitz.com/edit/angular-4dgxqh

0
JEY