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


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

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 .


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));
                }
            });
        }
});
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.