AngularのngDefaultControlとは何ですか?質問する

AngularのngDefaultControlとは何ですか?質問する

いいえ、これは重複した質問ではありません。SO や Github には、このディレクティブを[(ngModel)]、フォームに含まれていないディレクティブを持つタグに追加するように指示する質問や問題が山ほどあります。追加しないと、エラーが発生します。

ERROR Error: No value accessor for form control with unspecified name attribute

わかりました。この属性をそこに置けばエラーはなくなります。でも、待ってください。誰もそれが何をするのか知りません。そして、Angular のドキュメントにはそれについてまったく触れられていません。必要がないとわかっているのに、なぜ値アクセサーが必要なのでしょうか? この属性は値アクセサーとどのように関連しているのでしょうか? このディレクティブは何をしますか? 値アクセサーとは何ですか? どのように使用しますか?

そして、なぜ誰もがまったく理解していないことを続けるのでしょうか? このコード行を追加するだけで動作します。ありがとうございます。これは良いプログラムを書く方法ではありません。

そして、Angular のフォームに関する1 つではなく2 つの大きなガイドと、次のセクションを読みましたngModel

ところで、値アクセサや については何も言及されていませんngDefaultControl。どこにあるのでしょうか?

ベストアンサー1

[ngデフォルトコントロール]

サードパーティのコントロールでは、ControlValueAccessorAngular フォームで機能するために が必要です。Polymer の など、多くのコントロールはネイティブ要素<paper-input>のように動作するため<input>、 を使用できますDefaultValueAccessorngDefaultControl属性を追加すると、そのディレクティブを使用できるようになります。

<paper-input ngDefaultControl [(ngModel)]="value>

または

<paper-input ngDefaultControl formControlName="name">

これがこの属性が導入された主な理由です。

これは、angular2 のアルファ バージョンではng-default-control属性と呼ばれていました。

DefaultValueAccessorngDefaultControlディレクティブのセレクターの 1 つも同様です。

@Directive({
  selector:
      'input:not([type=checkbox])[formControlName],
       textarea[formControlName],
       input:not([type=checkbox])[formControl],
       textarea[formControl],
       input:not([type=checkbox])[ngModel],
       textarea[ngModel],
       [ngDefaultControl]', <------------------------------- this selector
  ...
})
export class DefaultValueAccessor implements ControlValueAccessor {

それはどういう意味ですか?

これは、独自の値アクセサーを持たない要素 (ポリマー コンポーネントなど) にこの属性を適用できることを意味します。したがって、この要素は から動作を取得しDefaultValueAccessor、この要素を Angular フォームで使用できます。

そうでなければ、独自の実装を用意する必要がありますControlValueAccessor

コントロール値アクセサ

Angularドキュメントには次のように記載されています

ControlValueAccessor は、Angular フォーム API と DOM 内のネイティブ要素間のブリッジとして機能します。

シンプルな Angular2 アプリケーションで次のテンプレートを記述してみましょう。

<input type="text" [(ngModel)]="userName">

上記の動作を理解するには、inputこの要素にどのディレクティブが適用されるかを知る必要があります。ここで、Angular はエラーとともにヒントを提供します。

処理されない Promise 拒否: テンプレート解析エラー: 'input' の既知のプロパティではないため、'ngModel' にバインドできません。

さて、SOを開いて答えを得ることができます:FormsModuleにインポートします@NgModule:

@NgModule({
  imports: [
    ...,
    FormsModule
  ]
})
export AppModule {}

インポートすると、すべて意図したとおりに動作します。しかし、内部では何が起こっているのでしょうか?

FormsModule は次のディレクティブをエクスポートします。

@NgModule({
 ...
  exports: [InternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES]
})
export class FormsModule {}

ここに画像の説明を入力してください

調査の結果、3つの指令が適用されることがわかりました。input

  1. Ngコントロールステータス

    @Directive({ セレクター: '[formControlName],[ngModel],[formControl]', ... }) エクスポートクラス NgControlStatus は AbstractControlStatus を拡張します { ... }

  2. Ngモデル

    @Directive({ セレクタ: '[ngModel]:not([formControlName]):not([formControl])', プロバイダ: [formControlBinding], exportAs: 'ngModel' }) export class NgModel extends NgControl implements OnChanges,

  3. デフォルト値

    @Directive({ セレクタ: `input:not([type=checkbox])[formControlName], textarea[formControlName], input:not([type=checkbox])formControl], textarea[formControl], input:not([type=checkbox])[ngModel], textarea[ngModel],[ngDefaultControl]', ,,, }) export class DefaultValueAccessor implements ControlValueAccessor {

NgControlStatusng-validディレクティブは、、などのクラスを操作するだけなのでng-touchedng-dirtyここでは省略できます。


DefaultValueAccesstorNG_VALUE_ACCESSORプロバイダー配列にトークンを提供します:

export const DEFAULT_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => DefaultValueAccessor),
  multi: true
};
...
@Directive({
  ...
  providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultValueAccessor implements ControlValueAccessor {

NgModelディレクティブは、NG_VALUE_ACCESSOR同じホスト要素で宣言されたコンストラクター トークンを挿入します。

export NgModel extends NgControl implements OnChanges, OnDestroy {
 constructor(...
  @Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {

私たちの場合、NgModelを注入しますDefaultValueAccessor。そして、NgModel ディレクティブは共有setUpControl関数を呼び出します。

export function setUpControl(control: FormControl, dir: NgControl): void {
  if (!control) _throwError(dir, 'Cannot find control with');
  if (!dir.valueAccessor) _throwError(dir, 'No value accessor for form control with');

  control.validator = Validators.compose([control.validator !, dir.validator]);
  control.asyncValidator = Validators.composeAsync([control.asyncValidator !, dir.asyncValidator]);
  dir.valueAccessor !.writeValue(control.value);

  setUpViewChangePipeline(control, dir);
  setUpModelChangePipeline(control, dir);

  ...
}

function setUpViewChangePipeline(control: FormControl, dir: NgControl): void 
{
  dir.valueAccessor !.registerOnChange((newValue: any) => {
    control._pendingValue = newValue;
    control._pendingDirty = true;

    if (control.updateOn === 'change') updateControl(control, dir);
  });
}

function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
  control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
    // control -> view
    dir.valueAccessor !.writeValue(newValue);

    // control -> ngModel
    if (emitModelEvent) dir.viewToModelUpdate(newValue);
  });
}

そして、これが橋が稼働している様子です。

ここに画像の説明を入力してください

NgModelコントロール(1)を設定し、dir.valueAccessor !.registerOnChangeメソッドを呼び出します。ControlValueAccessorコールバックをonChange(2)inputプロパティに保存し、イベントが発生したときにこのコールバックを起動します(3)。そして最後に、updateControlコールバック内で関数が呼び出されます(4)

function updateControl(control: FormControl, dir: NgControl): void {
  dir.viewToModelUpdate(control._pendingValue);
  if (control._pendingDirty) control.markAsDirty();
  control.setValue(control._pendingValue, {emitModelToViewChange: false});
}

ここで、Angular はフォーム API を呼び出しますcontrol.setValue

これが仕組みの簡単な説明です。

おすすめ記事