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

302

Я хочу следить за изменениями в словаре, но почему-то вызов 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 будет срабатывать каждый раз, когда имя или фамилия будут изменены, но этого не произойдет.

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

Теги:
angularjs-scope

8 ответов

572
Лучший ответ

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

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

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

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

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

  • 2
    Это хороший ответ, но я думаю, что бывают случаи, когда вы не захотите рекурсивно наблюдать за объектом.
  • 16
    Есть. По умолчанию, если вы не передадите значение true , будет проверяться равенство ссылок, если отслеживаемое значение является объектом или массивом. В этом случае вы должны явно указать свойства, такие как $scope.$watch('form.name') и $scope.$watch('form.surname')
Показать ещё 2 комментария
13

Объект 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++;
});
}
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
    Я думаю, что стоит упомянуть, что в этом случае вы потеряете функциональность доступа к старым значениям данных, которые вы храните. newVal , oldVal в этом примере - это временные метки, а не значения наблюдаемых объектов. Это не делает этот ответ недействительным, я просто думаю, что это недостаток, который стоит упомянуть.
  • 0
    Правда, этот метод я чаще всего использую, когда хочу загрузить сложную структуру данных в качестве источника чего-либо (например, новую версию некоторых данных). Если я забочусь о новых и старых значениях, я не буду хранить их в больших сложных объектах, скорее, у меня будет небольшое текстовое представление того, что меня волнует, и я буду следить за его изменениями или получу его при изменении метки времени.
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);
});
3

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

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

вы должны внести изменения в $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
    Отвечая на двухлетний вопрос с ответом, который является почти точной копией существующего? Не круто.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню