$观看对象


317

我想监视字典中的更改,但是由于某种原因,未调用监视回调。

这是我使用的控制器:

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

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

这是小提琴

我希望每次更改名称或姓氏时都会触发$ watch回调,但是不会发生。

正确的方法是什么?


Answers:


570

调用$watchwith true作为第三个参数:

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

默认情况下,在JavaScript中比较两个复杂对象时,将检查它们是否具有“引用”相等性,即询问两个对象是否引用同一事物,而不是“值”相等性,后者检​​查这些对象的所有属性的值对象是相等的。

根据Angular文档,第三个参数用于objectEquality

objectEquality == true为时,根据angular.equals函数确定watchExpression的不等式。为了保存对象的值以供以后比较,使用了该angular.copy函数。因此,这意味着观看复杂的对象将对内存和性能产生不利影响。


2
这是一个很好的答案,但是我想有时候您不希望递归地观察对象
Jason

16
有。默认情况下,如果您不通过true,则它将监视引用的相等性(如果监视的值是对象或数组)。在这种情况下,您应该明确指定属性,例如$scope.$watch('form.name')$scope.$watch('form.surname')
rossipedia

3
谢谢。那正是我想念的。Jason,您是对的-您并不总是希望观察递归对象,但就我而言,这正是我所需要的。
弗拉基米尔·西多连科

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在这个例子中是时间戳值不是观察对象的值。它不会使这个答案无效,我只是认为这是一个值得一提的缺点。
米哈尔Schielmann

的确,当我想加载复杂的数据结构作为某物(例如某些数据的新版本)的源时,我通常会使用此方法。如果我关心新旧值,则不会将它们存储在大的复杂对象中,而是会以小文本表示我关心的事物,并观察其变化或在时间戳更改时获取它。
Ferenc Takacs,2016年

5

您的代码不起作用的原因是因为$watch默认情况下会进行引用检查。因此,简而言之,请确保传递给它的对象是新对象。但是在您的情况下,您只是在修改表单对象的某些属性,而不创建新属性。为了使其正常工作,您可以true作为第三个参数传递。

$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
回答一个已有两年历史的问题,答案几乎与现有答案完全相同?不酷
贾里德·史密斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.