$ auf Datenänderungen in einer Angular-Direktive achten

132

Wie kann ich eine $watchVariable in einer Angular-Direktive auslösen, wenn ich die darin enthaltenen Daten bearbeite (z. B. Daten einfügen oder entfernen), dieser Variablen jedoch kein neues Objekt zuweisen?

Ich habe einen einfachen Datensatz, der gerade aus einer JSON-Datei geladen wird. Mein Angular-Controller erledigt dies und definiert einige Funktionen:

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

    };
});

Ich habe eine <button>, die die $scope.addFunktion richtig aufgerufen hat , und das neue Objekt wird richtig in die $scope.dataMenge eingefügt . Eine von mir eingerichtete Tabelle wird jedes Mal aktualisiert, wenn ich auf die Schaltfläche "Hinzufügen" klicke.

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

Eine von mir eingerichtete Direktive $scope.datawird jedoch nicht ausgelöst, wenn dies alles passiert.

Ich definiere mein Tag in HTML:

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

Was mit der folgenden Richtlinie verbunden ist (auf Fragen der Vernunft getrimmt):

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

Ich erhalte die "I see a data change!"Nachricht ganz am Anfang, aber nie danach, wenn ich auf die Schaltfläche "Hinzufügen" klicke.

Wie kann ich das $watchEreignis auslösen , wenn ich nur Objekte zum dataObjekt hinzufüge / daraus entferne und dem Objekt kein ganz neues Dataset zuweisen datakann?

Böser Schrankaffe
quelle
5
Nur ein kurzer Tipp, wenn newValue jemals gleich 0, false, "" oder etwas Ähnlichem ist. Ihr "Ich sehe eine Datenänderung!" wird nicht feuern. Zum Beispiel war der ursprüngliche Wert wahr, der neue Wert ist falsch. Es sollte ausreichen, nur newValue zu verwenden, damit die Uhr nur aufgerufen wird, wenn sich etwas geändert hat. Wenn es sich um ein Objekt handelt, wird es sofort aufgerufen.
Mathew Berg
Guter Tipp, danke! Ich werde die ifAussage vertauschen.
Evil Closet Monkey

Antworten:

218

Sie müssen die Prüfung auf Verschmutzung durch tiefe Objekte aktivieren. Standardmäßig überprüft Angular nur die Referenz der Variablen der obersten Ebene, die Sie beobachten.

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

siehe Geltungsbereich . Der dritte Parameter der $ watch-Funktion ermöglicht eine gründliche Überprüfung, ob er auf true gesetzt ist.

Beachten Sie, dass eine gründliche schmutzige Prüfung teuer ist . Wenn Sie also nur das untergeordnete Array anstelle der gesamten dataVariablen überwachen müssen, überwachen Sie die Variable direkt.

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

Version 1.2.x führte $ watchCollection ein

Shallow überwacht die Eigenschaften eines Objekts und wird ausgelöst, wenn sich eine der Eigenschaften ändert (bei Arrays bedeutet dies, dass die Array-Elemente überwacht werden; bei Objektzuordnungen bedeutet dies, dass die Eigenschaften überwacht werden).

scope.$watchCollection('val.children', function(newValue, oldValue) {});
Liviu T.
quelle
1
Arbeitet großartig. Danke dir!
Evil Closet Monkey
1
Froh, dass ich Helfen kann. Experimentieren Sie weiter mit der $ watch-Funktion, da die Ausdrücke, die sie beobachten kann, sehr unterschiedlich sind.
Liviu T.
1
Vielen Dank für den Hinweis, wie man nur tiefe Arrays überprüft;)
veritas
Es ist schade, dass ich nicht mehr als einmal abstimmen kann. Ich würde dir ein Kopfgeld geben, für die 2 Stunden habe ich verloren, danach zu suchen.
Alex Arvanitidis
beste Antwort da draußen.
Chovy
2

Denn wenn Sie Ihre Daten mit Deep of It auslösen möchten, müssen Sie das 3. Argument trueIhres Listeners übergeben. Standardmäßig falsewird Ihre Funktion nur dann ausgelöst, wenn sich Ihre Variable ändert, nicht das Feld .

Hazarapet Tunanyan
quelle
1

Meine Version für eine Direktive, die jqplot verwendet, um die Daten zu zeichnen, sobald sie verfügbar sind:

    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
quelle