$ obserwowanie zmian danych w dyrektywie Angular

133

Jak mogę wyzwolić $watchzmienną w dyrektywie Angular podczas manipulowania danymi wewnątrz (np. Wstawianie lub usuwanie danych), ale nie przypisywać nowego obiektu do tej zmiennej?

Mam prosty zestaw danych, który jest obecnie ładowany z pliku JSON. Mój kontroler Angular robi to, a także definiuje kilka funkcji:

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

    };
});

Mam taką, <button>która poprawnie nazywa się $scope.addfunkcją, a nowy obiekt jest poprawnie wstawiony do $scope.datazestawu. Utworzona przeze mnie tabela aktualizuje się za każdym razem, gdy naciskam przycisk „dodaj”.

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

Jednak dyrektywa, którą ustawiłem, aby obserwować, $scope.datanie zostanie zwolniona, gdy to wszystko się stanie.

Definiuję swój tag w HTML:

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

Który jest powiązany z następującą dyrektywą (przycięta ze względu na rozsądek pytania):

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!"Wiadomość dostaję na samym początku, ale nigdy po naciśnięciu przycisku „dodaj”.

Jak mogę wyzwolić $watchzdarzenie, kiedy tylko dodam / usuwam obiekty z dataobiektu, nie otrzymując zupełnie nowego zestawu danych do przypisania do dataobiektu?

Zła małpa w szafie
źródło
5
Szybka wskazówka, jeśli newValue kiedykolwiek będzie równa 0, false, „” lub cokolwiek podobnego, Twoje „Widzę zmianę danych!” nie odpali. Na przykład oryginalna wartość to true, a newValue to false. Powinno wystarczyć użycie w tym miejscu newValue, aby zegarek był wywoływany tylko wtedy, gdy coś się zmieniło. Jeśli jest to obiekt, zostanie natychmiast wywołany.
Mathew Berg,
Dobra wskazówka, dziękuję! Przełączę ifoświadczenie.
Evil Closet Monkey,

Odpowiedzi:

219

Musisz włączyć głębokie sprawdzanie zabrudzenia obiektów. Domyślnie kątowe sprawdza tylko odniesienie do obserwowanej zmiennej najwyższego poziomu.

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

patrz Zakres . Trzeci parametr funkcji $ watch umożliwia głębokie sprawdzenie, czy jest ustawiony na true.

Zwróć uwagę, że dokładne sprawdzenie jest kosztowne . Więc jeśli chcesz po prostu obserwować tablicę dzieci zamiast całej datazmiennej, obserwuj zmienną bezpośrednio.

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

wersja 1.2.x wprowadziła $ watchCollection

Shallow obserwuje właściwości obiektu i uruchamia się za każdym razem, gdy zmienia się którakolwiek z właściwości (w przypadku tablic oznacza to obserwację elementów tablicy; w przypadku map obiektów oznacza to obserwację właściwości)

scope.$watchCollection('val.children', function(newValue, oldValue) {});
Liviu T.
źródło
2
Działa świetnie. Dziękuję Ci!
Evil Closet Monkey,
1
Miło, że mogłem pomóc. Kontynuuj eksperymentowanie z funkcją $ watch, ponieważ wyrażenia, które może oglądać, są dość zróżnicowane.
Liviu T.,
1
Dzięki za odnotowanie, jak sprawdzić tylko głębokie tablice;)
veritas
Szkoda, że ​​nie mogę zagłosować więcej niż jeden raz. Dałbym ci nagrodę, za 2h straciłem na szukaniu tego.
Alex Arvanitidis
najlepsza odpowiedź.
chovy
2

Bo jeśli chcesz wywołać swoje dane głęboko w sobie, musisz przekazać trzeci argument trueswojego listenera. Domyślnie jest to falsei oznacza to, że funkcja zostanie wyzwolona tylko wtedy, gdy zmienna zmieni się, a nie pole .

Hazarapet Tunanyan
źródło
1

Moja wersja dla dyrektywy, która używa jqplot do wykreślania danych, gdy staną się dostępne:

    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
źródło