AngularJSでモデルの変更を監視するときに、初期ロードを無視するにはどうすればよいですか?


184

$ scope.fieldcontainerプロパティの深いグラフとして表示される単一のエンティティのエディターとして機能するWebページがあります。($ resourceを介して)REST APIから応答を受け取った後、 'fieldcontainer'にウォッチを追加します。このウォッチを使用して、ページ/エンティティが「ダーティ」かどうかを検出しています。現在、保存ボタンをバウンスさせていますが、ユーザーがモデルを汚すまで保存ボタンを非表示にしたいのです。

私が取得しているのは、時計の単一トリガーです。これは、.fieldcontainer = ...割り当てが時計を作成した直後に行われるために発生していると思います。最初の誤警報を吸収するために「dirtyCount」プロパティを使用することを考えていましたが、それは非常にハックに感じられます...そしてこれを処理するには「角度慣用的」な方法が必要であると考えました-私だけではありません汚れたモデルを検出するために時計を使用する。

時計を設定するコードは次のとおりです。

 $scope.fieldcontainer = Message.get({id: $scope.entityId },
            function(message,headers) {
                $scope.$watch('fieldcontainer',
                    function() {
                        console.log("model is dirty.");
                        if ($scope.visibility.saveButton) {
                            $('#saveMessageButtonRow').effect("bounce", { times:5, direction: 'right' }, 300);
                        }
                    }, true);
            });

「UI(dirtyCount> 0)」で私の「UIダーティ」コードを保護するよりも、これを行うためのよりクリーンな方法があるはずだと考え続けています...


また、特定のボタンに基づいて$ scope.fieldcontainerをリロードできるようにする必要もあります(エンティティの以前のバージョンのロードなど)。このため、私は時計を一時停止し、リロードしてから時計を再開する必要があります。
Kevin Hoffman

私の回答を承認済みの回答に変更することを検討しますか?時間をかけてそれまで読んでいる人は、それが正しい解決策であることに同意する傾向があります。
trixtur

@trixtur私は同じOP問題を抱えていますが、私の場合、私の古い値がでないため、あなたの答えは機能しませんundefined。これには、モデルの更新ですべての情報が表示されない場合に必要なデフォルト値があります。したがって、一部の値は変更されませんが、トリガーする必要があります。
oliholz 2015年

@oliholzしたがって、未定義に対してチェックする代わりに、「typeof」を取り出して、old_fieldcontainerをデフォルト値に対してチェックします。
trixtur

@KevinHoffmanあなたはこの質問を見つけた他の人々のために正しい答えにMWの答えをマークするべきです。それははるかに優れたソリューションです。ありがとう!
Dev01

回答:


118

初期ロードの直前にフラグを設定し、

var initializing = true

そして最初の$ watchが発火したら、

$scope.$watch('fieldcontainer', function() {
  if (initializing) {
    $timeout(function() { initializing = false; });
  } else {
    // do whatever you were going to do
  }
});

フラグは現在のダイジェストサイクルの最後に破棄されるため、次の変更はブロックされません。


17
はい、それはうまくいきますが、@ MWによって提案された新旧の価値アプローチの比較 よりシンプルで、より優れたAngularイディオマティックです。承認された回答を更新できますか?
gerryster 2014年

2
最初の発砲時に常にoldValueをnewValueに等しく設定することは、このフラグハックを行わなくて済むようにするために特に追加されました
Charlie Martin

2
古い値への新しい値は、古い値が定義されていない場合を処理しないため、MWの答えは実際には正しくありません。
trixtur

1
コメンターの場合:これは神のモデルではありません。「初期化された」変数をデコレーターのスコープ内に配置し、それで「通常の」時計を装飾することを妨げるものはありません。いずれにせよ、それはこの質問の範囲から完全に外れています。
2016

1
.get()コールバックでのウォッチャーの設定とスコープの初期化の両方で、提案されたメソッドの比較については、plnkr.co / edit / VrWrHHWwukqnxaEM3J5iを参照してください。
2016

483

リスナーが初めて呼び出されるとき、古い値と新しい値は同じになります。だからこれをしてください:

$scope.$watch('fieldcontainer', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // do whatever you were going to do
  }
});

これは実際にはAngularドキュメントがそれを処理することを推奨する方法です:

ウォッチャーがスコープに登録された後、リスナーfnが非同期で($ evalAsyncを介して)呼び出され、ウォッチャーが初期化されます。まれに、watchExpressionの結果が変化しないときにリスナーが呼び出されるため、これは望ましくありません。リスナーfn内でこのシナリオを検出するには、newValとoldValを比較できます。これら2つの値が同一(===)の場合、初期化のためにリスナーが呼び出されました


3
この方法は、書き換えの答え@より慣用的である
イジーVypědřík

25
これは正しくありません。ポイントは、nullからの初期ロード値への変更を無視することであり、変更がない場合の変更を無視しないことです。
2014年

1
ウォッチが作成されると、リスナー関数は常にトリガーされます。これは、最初の呼び出しを無視します。これは、質問者が望んでいたと私が信じているものです。古い値がnullだったときの変更を特に無視したい場合は、もちろんoldValueをnullと比較できます...しかし、彼がそうしたいと思っていません。
MW。

OPは、リソース(RESTサービスから)のウォッチャーについて具体的に尋ねています。最初にリソースが作成され、次にウォッチャーが適用され、次に応答からの属性が書き込まれ、それからAngularが循環します。「initializing」フラグを使用して最初のサイクルをスキップすると、初期化されたリソースにのみウォッチャーを適用できますが、/からの変更を引き続き監視できnullます。
2014

7
これは間違っていoldValueます。最初の回避策ではnullになります。
Kevin C.

42

私はこの質問に答えたと思いますが、提案があります:

$scope.$watch('fieldcontainer', function (new_fieldcontainer, old_fieldcontainer) {
    if (typeof old_fieldcontainer === 'undefined') return;

    // Other code for handling changed object here.
});

フラグを使用しても機能しますが、コードに少し匂いがありますね。


1
これが方法です。Coffeescriptでは、return unless oldValue?(?はCSの存在演算子です)と同じくらい簡単です。
Kevin C.

1
ワードコードの匂いの小道具!神オブジェクトlolもチェックしてください。あまりにも良い。
マシューハーウッド

1
これは受け入れられる答えではありませんが、APIからオブジェクトを生成するときに私のコードを機能させ、さまざまな保存状態を監視したいと考えています。非常に素晴らしい。
Lucas Reppe Welander 2016年

1
ありがとう-これは私に役立ちました
Wand Maker

1
これは素晴らしいですが、私の場合、APIへのAJAX呼び出しを行う前にデータを空の配列としてインスタンス化したため、型ではなく長さを確認します。しかし、この答えは少なくとも最も効果的な方法を確実に示しています。
Saborknight 2017年

4

現在の値の初期ロード中、古い値フィールドは未定義です。したがって、以下の例は、初期ロードを除外するのに役立ちます。

$scope.$watch('fieldcontainer', 
  function(newValue, oldValue) {
    if (newValue && oldValue && newValue != oldValue) {
      // here what to do
    }
  }), true;

!あなたは、おそらく以来==使用したいnullundefined(この状況で一致する、または'1' == 1など)
ジェイミー・パテ

一般的にあなたは正しいです。ただし、その場合、以前のチェックのため、newValueとoldValueをそのようなコーナー値にすることはできません。同じフィールドに属しているため、どちらの値も同じタイプです。ありがとうございました。
Tahsin Turkoz 2017

3

新しいvalの状態を有効にします。

$scope.$watch('fieldcontainer',function(newVal) {
      if(angular.isDefined(newVal)){
          //Do something
      }
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.