$ watchオブジェクト


317

辞書の変更を監視したいのですが、何らかの理由で監視コールバックが呼び出されません。

ここに私が使用するコントローラーがあります:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    $scope.$watch('form', function(newVal, oldVal){
        console.log('changed');
    });
}

こちらがバイオリンです。

名前または姓が変更されるたびに$ watchコールバックが呼び出されることを期待していますが、それは起こりません。

それを行う正しい方法は何ですか?


回答:


570

コール$watchtrue3番目の引数として:

$scope.$watch('form', function(newVal, oldVal){
    console.log('changed');
}, true);

デフォルトでは、JavaScriptで2つの複雑なオブジェクトを比較するときに、「参照」の等価性がチェックされ、2つのオブジェクトが同じものを参照しているかどうかが尋ねられます。「値」の等価性ではなく、それらのすべてのプロパティの値がチェックされますオブジェクトは等しいです。

パー角度ドキュメンテーション、3番目のパラメータがためのものですobjectEquality

の場合objectEquality == true、watchExpressionの不等式はangular.equals関数によって決まります。後で比較するためにオブジェクトの値を保存するにangular.copyは、関数を使用します。したがって、これは、複雑なオブジェクトを監視すると、メモリとパフォーマンスに悪影響を及ぼすことを意味します。


2
これは良い答えですが、オブジェクトを再帰的に見たくない場合があると思います
Jason

16
がある。デフォルトでは、を渡さないtrue場合、監視される値がオブジェクトまたは配列であるかどうかを参照の等価性についてチェックします。その場合、あなたは次のように、明示的にプロパティを指定する必要があります$scope.$watch('form.name')し、$scope.$watch('form.surname')
rossipedia

3
ありがとう。それはまさに私が逃したものです。ジェイソン、その通りです-常に再帰的なオブジェクトを監視する必要はありませんが、私の場合はまさにそれが必要でした。
Vladimir Sidorenko 2013年

1
ImmutableJSと参照比較は、パフォーマンスの向上に役立つ場合があります。
Dragos Rusu

13

formオブジェクトは、変更されていないnameプロパティがあります

更新されたフィドル

function MyController($scope) {
$scope.form = {
    name: 'my name',
}

$scope.changeCount = 0;
$scope.$watch('form.name', function(newVal, oldVal){
    console.log('changed');
    $scope.changeCount++;
});
}

12

リトルパフォーマンスの先端誰かがキーとサービスのデータストアの種類を持っている場合- >値のペア:

dataStoreというサービスがある場合は、ビッグデータオブジェクトが変更されるたびにタイムスタンプを更新できます。この方法では、オブジェクト全体を詳細に監視するのではなく、変更のタイムスタンプのみを監視します。

app.factory('dataStore', function () {

    var store = { data: [], change: [] };

    // when storing the data, updating the timestamp
    store.setData = function(key, data){
        store.data[key] = data;
        store.setChange(key);
    }

    // get the change to watch
    store.getChange = function(key){
        return store.change[key];
    }

    // set the change
    store.setChange = function(key){
        store.change[key] = new Date().getTime();
    }

});

そしてディレクティブでは、変更するタイムスタンプのみを監視しています

app.directive("myDir", function ($scope, dataStore) {
    $scope.dataStore = dataStore;
    $scope.$watch('dataStore.getChange("myKey")', function(newVal, oldVal){
        if(newVal !== oldVal && newVal){
            // Data changed
        }
    });
});

1
この場合、保存しているデータの古い値にアクセスする機能が失われることに言及する価値があると思います。 newValoldValこの例では、観測されたオブジェクトの値ではなく、タイムスタンプの値です。この回答が無効になるわけではありません。言及する価値のある欠点だと思います。
のMichałSchielmann

確かに、このメソッドは、複雑なデータ構造を何かのソース(たとえば、いくつかのデータの新しいバージョン)としてロードしたい場合に主に使用します。新しい値と古い値を気にする場合、それらを大きな複雑なオブジェクトに格納せず、気になるものの小さなテキスト表現を取得して、変更を監視するか、タイムスタンプが変更されたときに取得します。
Ferenc Takacs

5

コードが機能しない理由は$watch、デフォルトでは参照チェックを行うためです。つまり、簡単に言えば、渡されるオブジェクトが新しいオブジェクトであることを確認してください。しかし、あなたのケースでは、フォームオブジェクトのいくつかのプロパティを変更するだけで、新しいプロパティは作成しません。これを機能させるためにtrue、3番目のパラメーターとして渡すことができます。

$scope.$watch('form', function(newVal, oldVal){
    console.log('invoked');
}, true);

動作しますが$watchCollection、フォームオブジェクトの浅いプロパティを監視するため、$ watchよりも効率的な$ watchCollectionを使用できます。例えば

$scope.$watchCollection('form', function (newVal, oldVal) {
    console.log(newVal, oldVal);
});


1

これを試して:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    function track(newValue, oldValue, scope) {
        console.log('changed');
    };

    $scope.$watch('form.name', track);
}

0

オブジェクトの配列内のオブジェクトへの変更を監視したい人にとっては、これは私にとってはうまくいくように見えました(このページの他のソリューションではうまくいきませんでした):

function MyController($scope) {

    $scope.array = [
        data1: {
            name: 'name',
            surname: 'surname'
        },
        data2: {
            name: 'name',
            surname: 'surname'
        },
    ]


    $scope.$watch(function() {
        return $scope.data,
    function(newVal, oldVal){
        console.log(newVal, oldVal);
    }, true);

-3

$ watchを変更する必要があります....

function MyController($scope) {
    $scope.form = {
        name: 'my name',
    }

    $scope.$watch('form.name', function(newVal, oldVal){
        console.log('changed');
     
    });
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app>
    <div ng-controller="MyController">
        <label>Name:</label> <input type="text" ng-model="form.name"/>
            
        <pre>
            {{ form }}
        </pre>
    </div>
</div>


8
2年前の質問に既存の質問とほぼ同じである回答で答えますか?クールではありません。
Jared Smith
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.