$ смотреть объект

Я хочу следить за изменениями в словаре, но почему-то вызов callback не вызывается.

Вот контроллер, который я использую:

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

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

Вот скрипка: http://jsfiddle.net/Y8ByG/

Я ожидаю, что обратный вызов $watch будет срабатывать каждый раз, когда имя или фамилия будут изменены, но этого не произойдет.

Каков правильный способ сделать это?

Ответ 1

Вызовите $watch с true в качестве третьего аргумента:

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

По умолчанию при сравнении двух сложных объектов в JavaScript они будут проверяться на "ссылочное" равенство, которое спрашивает, относятся ли эти два объекта к одной и той же вещи, а не к равенству "значение", которое проверяет, соответствуют ли значения всех свойств этих объекты равны.

В соответствии с Угловой документацией третий параметр предназначен для objectEquality:

Когда objectEquality == true, неравенство watchExpression определяется в соответствии с функцией angular.equals. Чтобы сохранить значение объекта для последующего сравнения, angular.copy функция angular.copy. Это означает, что просмотр сложных объектов будет иметь неблагоприятные последствия для памяти и производительности.

Ответ 2

Объект form не изменяется, только свойство name

обновленный скрипт: http://jsfiddle.net/ReuA8/1/

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

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

Ответ 3

Маленький показатель производительности, если у кого-то есть служба данных с хранилищем данных с парами ключ → значение:

Если у вас есть служба с именем 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
        }
    });
});

Ответ 4

Причина, по которой ваш код не работает, заключается в том, что $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);
});

Ответ 5

Поскольку вы ищете изменения объекта формы, лучшим способом наблюдения является использование $watchCollection. Пожалуйста, ознакомьтесь с официальной документацией для разных характеристик производительности. https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

Ответ 6

Попробуйте следующее:

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

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

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

Ответ 7

Для тех, кто хочет следить за изменением объекта в массиве объектов, это, казалось, работает для меня (как это делали другие решения на этой странице):

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

Ответ 8

вы должны внести изменения в $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>