web-dev-qa-db-ja.com

(ngModelChange)特定の入力のUIを更新しません

ユーザーが何かのレートを入力できる入力フィールドがあります。

ユーザーが値を入力したら、四捨五入して表示し、更新された値をバッキングモデルのtsファイルに保存します。

Angularパイプの使用は、一方向のパイプと更新された値がモデルに反映されないため、これには適していません。

双方向にするために、私は次のことをしています:

<input type="text" [ngModel]="model.rate" (ngModelChange)="model.rate=roundRate($event)" name="rate" />

roundDate関数は次のようになります

roundRate(value) {
    return Math.round(value);
}

これで、5.6、7.8、9.5などの値を入力すると、それらはすべて四捨五入され、モデルにそれぞれ表示され、6、8、および10に格納されます。これは、予想される動作です。

2.1、3.4、5.3などと入力すると問題が発生します。この場合、roundRate関数が呼び出され、四捨五入後に値が返されます。ただし、画面に表示される値は古い値です(2.1、3.4、5.3)。

Chrome=のinput要素を調べたところ、ng-reflect-modelプロパティは期待値(2、3、5)に更新されていました。

<input _ngcontent-c39="" name="rate" type="text" ng-reflect-name="rate" ng-reflect-model="5" class="ng-valid ng-dirty ng-touched">

誰かがここで何が起こっているのか説明してもらえますかng-reflect-modelプロパティが更新され、画面にまだ古い値が表示されるのはなぜですか?

ご協力いただきありがとうございます!

4
Yogesh

よりクリーンな実装では、_(change)_ _@Output_プロパティと[(ngModel)]を使用するだけです。 roundRateの実装により、次のように変更されます。

_import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  model = { rate: null };

  roundRate() {
    this.model.rate = Math.round(+this.model.rate);
  }
}
_

そしてテンプレートでは:

_<input type="text" [(ngModel)]="model.rate" (change)="roundRate()" name="rate" />
_

PS:これは、入力フィールドからblurを実行したときにのみ値を更新します


これが、参考のための Sample StackBlitz です。

2
SiddAjmera

ngModelディレクティブAPI を詳しく見てみると、ngModel@Input binding であり、一部の値をmodel変数として受け入れることがわかります。 ngModelコントロールの初期化時に FormControlを作成 暗黙的に。

public readonly control: FormControl = new FormControl();

model値を更新する魔法は ngOnChangesフック から発生するため、ビューの値をmodel値と同期します。 ngOnChangesフックをよく見ると、入力が検証され、他のチェックも適用されます。その後、ngModel値の値が実際にisPropertyUpdatedメソッドを使用して変更されているかどうかが厳密にチェックされます。

ngOnChanges -ngModelディレクティブ

ngOnChanges(changes: SimpleChanges) {
    this._checkForErrors();
    if (!this._registered) this._setUpControl();
    if ('isDisabled' in changes) {
      this._updateDisabled(changes);
    }

    if (isPropertyUpdated(changes, this.viewModel)) {
      this._updateValue(this.model); // helps to update 
      this.viewModel = this.model;
    }
}

private _updateValue(value: any): void {
    resolvedPromise.then(() => { 
      // set value will set form control
      this.control.setValue(value, {emitViewToModelChange: false}); 
    });
}

しかし、それを実現するにはAngularは変更検出サイクル中に変更を認識する必要があります。モデルを変更していないため、ngOnChangesフックはトリガーされません。

enter image description here

これまでに説明したのはAPI部分です。質問に戻りましょう。

以下を試してください stackblitzのスニペット 、私たちの期待はどうなるでしょう。入力値の変更時に、その値を10自体に設定する必要があります。

<input type="text" 
  [ngModel]="model.rate" 
  (ngModelChange)="model.rate = 10"
  name="rate" />

残念ながら、それはそのような方法では起こりません。最初に数値を入力すると、入力値が10に変更され、後で入力するものはすべて数値入力に追加され続けることがわかります。ああ、なぜだろう?

元の質問に戻りましょう

<input type="text" 
  [ngModel]="model.rate" 
  (ngModelChange)="model.rate=roundRate($event)"
  name="rate" />
 {{model.rate}}

ngModelは一方向バインディングとして使用されます。期待される動作は、model.rateに割り当てられた値がinput内に反映されていることです。 1.1と入力してみましょう。値1.1が表示されます。次に、1.2を入力してみてください。1.2の結果は1になります。なぜだろう?しかし確かにmodel.rateバインディングは正しく更新されます。同様に、4.64.8をチェックすると、結果は5となり、完全に機能します。

上記の例を分解してみましょう。1.1を入力するとどうなりますか。

  1. タイプ1 => model.rateは1になります
  2. タイプ1. => model.rateは1のままです
  3. タイプ1.1 => model.rateは1のままです

1.1値を入力したため、最終的に1.2または1を入力すると、モデル値はMath.roundのままになります。これはmodel.rateの値を更新しないため、これ以降に入力したものはすべてngModelバインディングによって無視され、inputフィールド内に表示されます。

4.6を確認します。4.8は完全に機能します。どうして?それを段階的に分解する

  1. タイプ4 => model.rateは4になります
  2. タイプ4. => model.rateは4のままです
  3. タイプ4.6 => model.ratebecomes5

ここで、テキストボックスに4.6と入力すると、Math.roundの値は5になります。 model.rate(ngModel)の値が変更されると、inputフィールドの値が更新されます

ここで答えをもう一度読み始めます。最初に説明した技術的な側面もクリアされます。

注:回答を読んでいる間、スニペットに提供されているリンクをたどりますが、それを理解するのに役立つ場合があります。


これを機能させるには、blur/changeのフィールドを更新してみてください。このイベントでは、fieldsのフォーカスから Sid's answer のようにフォーカスが発生します。フィールドがフォーカスされたときに一度モデルを更新しているため、うまくいきました。

しかしそれは一度だけ動作します。常に更新を維持するために、いくつかのトリックを実行できます。

this.model.rate = new String(Math.round(value));

これにより、値を丸めるたびに新しいオブジェクト参照が生成されます。

Stackblitzのスニペット

7
Pankaj Parkar

Angular変更検出戦略では、UIに変更を反映するのに役立ちます。

以下のコードを使用してください:

ステップ1:コンポーネントにChangeDetectorRefをインポートします

import { ChangeDetectorRef} from angular/core';

ステップ2:コンストラクターにChangeDetectorRefのインスタンスを作成します。

constructor(private ref: ChangeDetectorRef){
}

ステップ3:値を更新している場所を呼び出します。

this.ref.detectChanges();
0
sanjay kumar

NgModel-を使用せずに$ eventを使用してその値を取得することもできます

component.html

<table>
<tbody>
<tr>
<td colspan="2" style="width:100px;height:38px;"><input type = "text"  (change)="enterDirectRowNumberResponse(data,$event.target.value)"></td></tr>
</tbody
</table>

component.ts

enterDirectRowNumberResponse(data,event){
        console.log ("Inside enterDirectRowNumberResponse",event)
        console.log ("data = ", data );
}
0
Techdive