$ surveiller les changements de données dans une directive angulaire

132

Comment puis-je déclencher une $watchvariable dans une directive angulaire lors de la manipulation des données à l'intérieur (par exemple, insérer ou supprimer des données), mais ne pas affecter un nouvel objet à cette variable?

J'ai un ensemble de données simple en cours de chargement à partir d'un fichier JSON. Mon contrôleur angulaire fait cela, ainsi que définir 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 $scope.addfonction, et le nouvel objet est correctement inséré dans l' $scope.dataensemble. Un tableau que j'ai mis en place est mis à 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 mise en place pour surveiller $scope.datan'est pas renvoyée lorsque tout cela se produit.

Je définis ma balise en HTML:

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

Qui est associé à la directive suivante (réduite pour la cohérence des questions):

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 "I see a data change!"message au tout début, mais jamais après que je clique sur le bouton «ajouter».

Comment puis-je déclencher l' $watchévénement lorsque je viens d'ajouter / supprimer des objets de l' dataobjet, sans obtenir un tout nouvel ensemble de données à attribuer à l' dataobjet?

Singe de placard maléfique
la source
5
Juste un petit conseil si newValue est égal à 0, false, "" ou toute autre chose du genre "Je vois un changement de données!" ne tirera pas. Par exemple, la valeur d'origine était true, la newValue est false. Il devrait être suffisant d'utiliser simplement newValue, cette montre n'est appelée que si quelque chose a changé. Si c'est un objet, il sera appelé instantanément.
Mathew Berg
Bon conseil, merci! Je vais changer la ifdéclaration.
Evil Closet Monkey

Réponses:

218

Vous devez activer la vérification approfondie des objets sales. Par défaut, angular vérifie uniquement 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 Portée . Le troisième paramètre de la fonction $ watch permet de vérifier en profondeur s'il est défini sur true.

Notez que la vérification en profondeur est coûteuse . Donc, si vous avez juste besoin de regarder le tableau des enfants au lieu de la datavariable entière , regardez la variable directement.

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

la version 1.2.x a introduit $ watchCollection

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

scope.$watchCollection('val.children', function(newValue, oldValue) {});
Liviu T.
la source
1
Fonctionne très bien. Je vous remercie!
Evil Closet Monkey
1
Heureux de vous aider. Continuez à expérimenter la fonction $ watch car les expressions qu'elle peut regarder sont assez variées.
Liviu T.20
1
Merci de noter comment vérifier uniquement les tableaux profonds;)
veritas
C'est dommage que je ne puisse pas voter plus d'une fois ... Je vous donnerais une prime, pour les 2h que j'ai perdues à chercher ça.
Alex Arvanitidis
meilleure réponse là-bas.
chovy
2

Parce que si vous voulez déclencher vos données en profondeur, vous devez passer le 3ème argument truede votre auditeur.Par défaut, c'est falseet il me semble que votre fonction déclenchera, uniquement lorsque votre variable changera et non son champ .

Hazarapet Tunanyan
la source
1

Ma version pour une directive qui utilise jqplot pour tracer les données une fois qu'elles deviennent 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));
                }
            });
        }
});
rob2universe
la source