$ observando alterações de dados em uma diretiva Angular


132

Como posso disparar uma $watchvariável em uma diretiva Angular ao manipular os dados internos (por exemplo, inserir ou remover dados), mas não atribuir um novo objeto a essa variável?

Atualmente, tenho um conjunto de dados simples sendo carregado de um arquivo JSON. Meu controlador Angular faz isso, bem como define algumas funções:

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

    };
});

Eu tenho uma <button>que é chamada corretamente de $scope.addfunção e o novo objeto é inserido corretamente no $scope.dataconjunto. Uma tabela que eu configurei é atualizada sempre que clico no botão "adicionar".

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

No entanto, uma diretiva que eu configurei para assistir $scope.datanão está sendo acionada quando tudo isso acontece.

Eu defino minha tag em HTML:

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

Que está associado à seguinte diretiva (aparada para sanidade das perguntas):

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

Recebo a "I see a data change!"mensagem logo no início, mas nunca depois de clicar no botão "adicionar".

Como posso acionar o $watchevento quando estou apenas adicionando / removendo objetos do dataobjeto, sem obter um conjunto de dados totalmente novo para atribuir ao dataobjeto?


5
Apenas uma dica rápida se newValue for igual a 0, false "" "ou qualquer outra coisa assim:" Eu vejo uma alteração de dados! " não vai atirar. Por exemplo, o valor original era true, o newValue é false. Deve ser suficiente usar newValue, o relógio só será chamado se algo mudar. Se for um objeto, será chamado instantaneamente.
— Mathew Berg

Boa dica, obrigado! Vou mudar a ifdeclaração.
— Evil Closet Monkey

Respostas:


218

Você precisa habilitar a verificação suja de objetos profundos. Por padrão, angular verifica apenas a referência da variável de nível superior que você assiste.

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

veja Escopo . O terceiro parâmetro da função $ watch permite uma verificação profunda e suja, se estiver definida como true.

Observe que a verificação suja profunda é cara . Portanto, se você precisar apenas observar a matriz filho em vez de toda a datavariável, observe a variável diretamente.

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

a versão 1.2.x introduziu $ watchCollection

Shallow observa as propriedades de um objeto e dispara sempre que alguma das propriedades muda (para matrizes, isso implica observar os itens da matriz; para mapas de objetos, isso implica observar as propriedades)

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

1
Trabalhando muito bem. Obrigado!
— Evil Closet Monkey

1
Feliz em ajudar. Continue experimentando a função $ watch, pois as expressões que ela pode assistir são bastante variadas.
— Liviu T.

1
Obrigado por notar como verificar matrizes única profundas;)
— veritas

É uma pena que eu não possa votar mais de uma vez .. Eu te daria uma recompensa, pelas 2h que perdi procurando por isso.
— Alex Arvanitidis

melhor resposta lá fora.
— chovy 19/07/2015

2

Como se você deseja acionar seus dados com profundidade, é necessário passar o terceiro argumento truedo seu ouvinte. Por padrão, é falsee me parece que sua função será acionada, apenas quando sua variável mudar, não é o campo .


1

Minha versão de uma diretiva que usa jqplot para plotar os dados assim que estiverem disponíveis:

    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));
                }
            });
        }
});
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.