エラー手段が、角度が何をすべきかわからないことをあなたは置くときformControl
にdiv
。これを修正するには、2つのオプションがあります。
- あなたは入れた
formControlName
箱の角アウトによってサポートされている要素、上。それらは:input
、textarea
およびselect
です。
ControlValueAccessor
インターフェースを実装します。そうすることで、Angularに「コントロールの値にアクセスする方法」(したがって名前)を伝えます。または簡単な言い方をすると:何をすべきか、formControlName
要素にを置くと、それに関連付けられた値が自然にはありません。
さて、ControlValueAccessor
インターフェースの実装は、最初は少し困難な場合があります。特に、これに関する優れたドキュメントはあまりなく、コードに多くのボイラープレートを追加する必要があるためです。したがって、これをいくつかの簡単な手順で分解してみましょう。
フォームコントロールを独自のコンポーネントに移動する
を実装するControlValueAccessor
には、新しいコンポーネント(またはディレクティブ)を作成する必要があります。フォームコントロールに関連するコードをそこに移動します。このように、それも簡単に再利用できます。既にコンポーネント内にコントロールがあることは、そもそもControlValueAccessor
インターフェースを実装する必要がある理由かもしれません。そうでなければ、カスタムコンポーネントをAngularフォームと一緒に使用することができないからです。
コードにボイラープレートを追加する
ControlValueAccessor
インターフェースの実装は非常に冗長です。これが付属するボイラープレートです。
import {Component, OnInit, forwardRef} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
styleUrls: ['./custom-input.component.scss'],
// a) copy paste this providers property (adjust the component name in the forward ref)
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
// b) Add "implements ControlValueAccessor"
export class CustomInputComponent implements ControlValueAccessor {
// c) copy paste this code
onChange: any = () => {}
onTouch: any = () => {}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
// d) copy paste this code
writeValue(input: string) {
// TODO
}
では、個々のパーツは何をしているのでしょうか?
- a)
ControlValueAccessor
インターフェイスを実装したことをランタイム中にAngularに知らせます
- b)
ControlValueAccessor
インターフェースを実装していることを確認します
- c)これはおそらく最も混乱しやすい部分です。基本的に何をやっていることは、あなたが角度をあなたのクラスのプロパティ/メソッドをオーバーライドするための手段を与えている
onChange
とonTouch
、それはあなたがそれらの機能を呼び出すことができるように、実行時に独自の実装、だと。したがって、この点を理解することが重要です。onChangeとonTouchを自分で実装する必要はありません(最初の空の実装以外)。(c)で行う唯一のことは、Angularに独自の関数をクラスにアタッチさせることです。どうして?そのため、Angularが提供するおよびメソッドを適切なタイミングで呼び出すことができます。以下でこれがどのように機能するかを見ていきます。onChange
onTouch
- d)また
writeValue
、次のセクションで、実装時にメソッドがどのように機能するかを確認します。ここに配置したので、必要なすべてのプロパティControlValueAccessor
が実装され、コードは引き続きコンパイルされます。
writeValueを実装する
何writeValue
が、フォームコントロールが外側で変更されたときに、カスタムコンポーネント内で何かを行うことです。したがって、たとえば、カスタムフォームコントロールコンポーネントに名前を付けて、app-custom-input
それを次のように親コンポーネントで使用しているとします。
<form [formGroup]="form">
<app-custom-input formControlName="myFormControl"></app-custom-input>
</form>
その後writeValue
、親コンポーネントが何らかの方法での値を変更するたびにトリガーされますmyFormControl
。これは、たとえば、フォーム(this.form = this.formBuilder.group({myFormControl: ""});
)の初期化中またはフォームのリセット時などthis.form.reset();
です。
フォームコントロールの値が外側で変化した場合に通常行うことは、フォームコントロールの値を表すローカル変数に書き込むことです。たとえばCustomInputComponent
、テキストベースのフォームコントロールを中心とする場合、次のようになります。
writeValue(input: string) {
this.input = input;
}
のhtmlでCustomInputComponent
:
<input type="text"
[ngModel]="input">
Angularのドキュメントで説明されているように、input要素に直接書き込むこともできます。
これで、何かが外部で変更されたときにコンポーネントの内部で何が起こるかを処理しました。次に、別の方向を見てみましょう。コンポーネントの内部で何かが変更されたときに、どのように外部の世界に通知しますか?
onChangeを呼び出す
次のステップは、内の変更について親コンポーネントに通知することCustomInputComponent
です。ここで、上記の(c)のonChange
およびonTouch
関数が機能します。これらの関数を呼び出すことで、コンポーネント内部の変更について外部に通知できます。値の変更を外部に伝達するには、新しい値を引数としてonChangeを呼び出す必要があります。たとえば、ユーザーがinput
カスタムコンポーネントのフィールドに何かを入力した場合onChange
、更新された値を使用して呼び出します。
<input type="text"
[ngModel]="input"
(ngModelChange)="onChange($event)">
上から実装(c)をもう一度確認すると、何が起こっているかがわかります。Angularは、onChange
クラスプロパティへの独自の実装です。この実装では、更新されたコントロール値である1つの引数が必要です。今行っているのは、そのメソッドを呼び出して、Angularに変更について知らせることです。Angularは先に進み、外側のフォーム値を変更します。これは、これらすべての重要な部分です。あなたはAngularにフォームコントロールをいつ更新するべきか、そしてを呼び出すことでどのような値で更新するかを伝えましたonChange
。「コントロール値にアクセスする」手段を与えました。
ちなみに名前onChange
は私が選んだものです。ここでは、たとえばpropagateChange
、同様のものを選択できます。ただし、どのような名前を付けても、Angularによって提供され、registerOnChange
実行時にメソッドによってクラスにバインドされる、1つの引数を取る関数と同じになります。
onTouchを呼び出す
フォームコントロールは「触れる」ことができるため、Angularにカスタムフォームコントロールがいつ触れられるかを理解する手段を与える必要もあります。onTouch
関数を呼び出すことで、それを実行できます。したがって、ここでの例で、Angularがすぐに使えるフォームコントロールに対して行っている方法に準拠したいonTouch
場合は、入力フィールドがぼやけているときに呼び出す必要があります。
<input type="text"
[(ngModel)]="input"
(ngModelChange)="onChange($event)"
(blur)="onTouch()">
繰り返しますが、onTouch
私が選んだ名前ですが、実際の機能はAngularによって提供され、引数はありません。これは、Angularにフォームコントロールが操作されたことを知らせるだけなので、理にかなっています。
すべてを一緒に入れて
それがすべて一緒になったとき、それはどのように見えますか?次のようになります。
// custom-input.component.ts
import {Component, OnInit, forwardRef} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-custom-input',
templateUrl: './custom-input.component.html',
styleUrls: ['./custom-input.component.scss'],
// Step 1: copy paste this providers property
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
// Step 2: Add "implements ControlValueAccessor"
export class CustomInputComponent implements ControlValueAccessor {
// Step 3: Copy paste this stuff here
onChange: any = () => {}
onTouch: any = () => {}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
// Step 4: Define what should happen in this component, if something changes outside
input: string;
writeValue(input: string) {
this.input = input;
}
// Step 5: Handle what should happen on the outside, if something changes on the inside
// in this simple case, we've handled all of that in the .html
// a) we've bound to the local variable with ngModel
// b) we emit to the ouside by calling onChange on ngModelChange
}
// custom-input.component.html
<input type="text"
[(ngModel)]="input"
(ngModelChange)="onChange($event)"
(blur)="onTouch()">
// parent.component.html
<app-custom-input [formControl]="inputTwo"></app-custom-input>
// OR
<form [formGroup]="form" >
<app-custom-input formControlName="myFormControl"></app-custom-input>
</form>
その他の例
ネストされたフォーム
コントロール値アクセサは、ネストされたフォームグループに適したツールではないことに注意してください。ネストされたフォームグループの場合は、@Input() subform
代わりに単に使用できます。コントロールバリューアクセサーは、ラップすることを意図したものcontrols
ではなく、groups
!ネストされたフォームの入力を使用する方法の例を参照してください:https : //stackblitz.com/edit/angular-nested-forms-input-2
出典