$ watch'ing для изменений данных в угловой директиве

132

Как я могу активировать $watchпеременную в директиве Angular при манипулировании данными внутри (например, вставляя или удаляя данные), но не назначать новый объект этой переменной?

У меня есть простой набор данных, который в настоящее время загружается из файла JSON. Мой контроллер Angular делает это, а также определяет несколько функций:

App.controller('AppCtrl', function AppCtrl($scope, JsonService) {
    // load the initial data model
    if (!$scope.data) {
        JsonService.getData(function(data) {
            $scope.data = data;
            $scope.records = data.children.length;
        });
    } else {
        console.log("I have data already... " + $scope.data);
    }

    // adds a resource to the 'data' object
    $scope.add = function() {
        $scope.data.children.push({ "name": "!Insert This!" });
    };

    // removes the resource from the 'data' object
    $scope.remove = function(resource) {
        console.log("I'm going to remove this!");
        console.log(resource);
    };

    $scope.highlight = function() {

    };
});

У меня есть функция, <button>которая правильно вызвала $scope.addфункцию, и новый объект правильно вставлен в $scope.dataнабор. Таблица, которую я установил, обновляется каждый раз, когда я нажимаю кнопку «добавить».

<table class="table table-striped table-condensed">
  <tbody>
    <tr ng-repeat="child in data.children | filter:search | orderBy:'name'">
      <td><input type="checkbox"></td>
      <td>{{child.name}}</td>
      <td><button class="btn btn-small" ng-click="remove(child)" ng-mouseover="highlight()"><i class="icon-remove-sign"></i> remove</button></td>
    </tr>
  </tbody>
</table>

Однако директива, которую я установил для наблюдения $scope.data, не запускается, когда все это происходит.

Я определяю свой тег в HTML:

<d3-visualization val="data"></d3-visualization>

Это связано со следующей директивой (обрезанной для разумности вопроса):

App.directive('d3Visualization', function() {
    return {
        restrict: 'E',
        scope: {
            val: '='
        },
        link: function(scope, element, attrs) {
            scope.$watch('val', function(newValue, oldValue) {
                if (newValue)
                    console.log("I see a data change!");
            });
        }
    }
});

Я получаю "I see a data change!"сообщение в самом начале, но никогда после того, как нажимаю кнопку «добавить».

Как я могу инициировать $watchсобытие, когда я просто добавляю / удаляю объекты из dataобъекта, а не получаю новый набор данных для назначения dataобъекту?

Злой шкаф обезьяны
источник
5
Просто небольшой совет, если newValue когда-либо будет равно 0, false "" или что-то еще в этом роде, ваше "Я вижу изменение данных!" не будет стрелять. Например, исходное значение было истинным, новое значение - ложным. Достаточно просто использовать здесь newValue, эти часы вызываются только в том случае, если что-то изменилось. Если это объект, он будет вызван немедленно.
Мэтью Берг
Хороший совет, спасибо! Я заменю ifзаявление.
Evil Closet Monkey

Ответы:

218

Вам необходимо включить глубокую проверку грязных объектов. По умолчанию angular проверяет только ссылку на переменную верхнего уровня, которую вы смотрите.

App.directive('d3Visualization', function() {
    return {
        restrict: 'E',
        scope: {
            val: '='
        },
        link: function(scope, element, attrs) {
            scope.$watch('val', function(newValue, oldValue) {
                if (newValue)
                    console.log("I see a data change!");
            }, true);
        }
    }
});

см. Область применения . Третий параметр функции $ watch включает глубокую грязную проверку, имеет ли он значение true.

Обратите внимание, что глубокая грязная проверка стоит дорого . Поэтому, если вам просто нужно наблюдать за дочерним массивом, а не за всей dataпеременной, следите за переменной напрямую.

scope.$watch('val.children', function(newValue, oldValue) {}, true);

версия 1.2.x представила $ watchCollection

Shallow наблюдает за свойствами объекта и срабатывает при изменении любого из свойств (для массивов это подразумевает наблюдение за элементами массива; для карт объектов это подразумевает наблюдение за свойствами)

scope.$watchCollection('val.children', function(newValue, oldValue) {});
Ливиу Т.
источник
1
Работает отлично. Спасибо!
Evil Closet Monkey
1
Рад помочь. Продолжайте экспериментировать с функцией $ watch, поскольку выражения, которые она может наблюдать, весьма разнообразны.
Ливиу Т.
1
Спасибо, что заметили, как проверять только глубокие массивы;)
veritas
Жаль, что я не могу проголосовать больше одного раза ... Я бы дал вам награду за 2 часа, которые я потерял в поисках этого.
Alex Arvanitidis
лучший ответ там.
chovy
2

Потому что, если вы хотите активировать свои данные с глубоким из них, вы должны передать 3-й аргумент trueвашего слушателя. По умолчанию falseэто означает, что ваша функция будет запускаться, только когда ваша переменная изменится, а не поле .

Азарапет Туанян
источник
1

Моя версия для директивы, которая использует jqplot для построения данных, когда они становятся доступными:

    app.directive('lineChart', function() {
        $.jqplot.config.enablePlugins = true;

        return function(scope, element, attrs) {
            scope.$watch(attrs.lineChart, function(newValue, oldValue) {
                if (newValue) {
                    // alert(scope.$eval(attrs.lineChart));
                    var plot = $.jqplot(element[0].id, scope.$eval(attrs.lineChart), scope.$eval(attrs.options));
                }
            });
        }
});
rob2universe
источник