$ смотреть изменения данных в директиве Angular

Как я могу вызвать переменную $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?

Ответ 1

Вам нужно включить глубокую проверку грязных объектов. По умолчанию 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);
        }
    }
});

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

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

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

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

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

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

Ответ 2

Моя версия для директивы, которая использует 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));
                }
            });
        }
});

Ответ 3

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