$ watch'ing на зміни даних у кутовій директиві


132

Як я можу викликати $watchзмінну в кутовій директиві, коли маніпулюють даними всередині (наприклад, вставляючи або видаляючи дані), але не призначаючи цьому об'єкту новий об'єкт?

У мене зараз простий набір даних завантажується з файлу JSON. Мій кутовий контролер робить це, а також визначає кілька функцій:

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 - хибним. Потрібно бути достатньо просто використовувати newValue там, щоб годинник викликав лише те, що щось змінилося. Якщо це об'єкт, він буде викликаний миттєво.
Меттью Берг

Гарна порада, дякую! Я переключу ifзаяву.
Evil Closet Monkey

Відповіді:


218

Потрібно включити глибоку брудну перевірку об'єктів. За замовчуванням кутова перевіряє лише посилання на змінну верхнього рівня, яку ви переглядаєте.

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

Дрібне спостереження за властивостями об'єкта і спрацьовування кожного разу, коли будь-яка з властивостей змінюється (для масивів це означає перегляд елементів масиву; для об'єктних карт це означає перегляд властивостей)

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

1
Працює чудово. Дякую!
Evil Closet Monkey

1
Радий допомогти. Продовжуйте експериментувати з функцією $ watch, оскільки вирази, які він може переглядати, дуже різноманітні.
Лівіу Т.

1
Дякую за те, що помітили, як перевірити лише глибокі масиви;)
veritas

Шкода, що я не можу подати заявку більше одного разу .. Я б дав тобі щедрість, бо за 2 години я втратив це.
Олексій Арванідідіс

найкраща відповідь там.
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));
                }
            });
        }
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.