web-dev-qa-db-ja.com

オブジェクトを見る

辞書の変更を監視したいのですが、何らかの理由でwatch callbackが呼び出されません。

これが私が使うコントローラーです:

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

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

これは フィドル です。

名前または姓が変更されるたびに$ watchコールバックが起動されると思いますが、発生しません。

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

308

3番目の引数としてtrueを指定して$watchを呼び出します。

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

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

Angular documentation によると、3番目のパラメータはobjectEqualityです。

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

554
rossipedia

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++;
});
}
13
Jason

少し パフォーマンスのヒント 誰かがキー - >値のペアでデータストアのようなサービスを受けている場合:

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

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
        }
    });
});
12
Ferenc Takacs

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

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

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

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

フォームオブジェクトの変更を探しているのであれば、最も効果的な方法はを使用することです。
$watchCollection。さまざまなパフォーマンス特性については、公式の ドキュメント をご覧ください。

4

これを試して:

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

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

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

オブジェクトの配列内のオブジェクトへの変更を監視したい人のために、これは私のために働くように見えた(このページの他の解決策はしなかったように):

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);
0
Max