web-dev-qa-db-fra.com

Surveille les modifications de données dans une directive Angular

Comment puis-je déclencher une variable $watch dans une directive Angular lors de la manipulation des données internes (par exemple, l'insertion ou la suppression de données), sans affecter un nouvel objet à cette variable?

J'ai un simple jeu de données en cours de chargement à partir d'un fichier JSON. Mon contrôleur Angular le fait, tout en définissant quelques fonctions:

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() {

    };
});

J'ai un <button> qui a correctement appelé la fonction $scope.add et le nouvel objet est correctement inséré dans l'ensemble $scope.data. Une table que j'ai configurée est mise à jour chaque fois que je clique sur le bouton "ajouter".

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

Cependant, une directive que j'ai configurée pour surveiller $scope.data n'est pas déclenchée lorsque tout cela se produit.

Je définis mon tag en HTML:

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

Qui est associé à la directive suivante (ajustée pour la santé de la question):

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!");
            });
        }
    }
});

Je reçois le message "I see a data change!" au tout début, mais jamais après en appuyant sur le bouton "ajouter".

Comment puis-je déclencher l'événement $watch lorsque j'ajoute/supprime simplement des objets de l'objet data, sans qu'un tout nouveau jeu de données soit affecté à l'objet data?

132
Evil Closet Monkey

Vous devez activer la vérification en profondeur de l'objet sale. Par défaut, angular ne vérifie que la référence de la variable de niveau supérieur que vous regardez.

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

voir Scope . Le troisième paramètre de la fonction $ watch active la vérification en profondeur si sa valeur est true.

Prenez note que la vérification en profondeur est coûteuse . Donc, si vous avez juste besoin de regarder le tableau d'enfants au lieu de la variable entière data, observez la variable directement.

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

version 1.2.x introduite $ watchCollection

Shallow surveille les propriétés d'un objet et se déclenche chaque fois que l'une de ces propriétés change (pour les tableaux, cela implique de surveiller les éléments du tableau; pour les mappes d'objets, cela implique de surveiller les propriétés).

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

Ma version pour une directive qui utilise jqplot pour tracer les données une fois qu'elles sont disponibles:

    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));
                }
            });
        }
});
1
RobE