$ assiste a um objeto


317

Quero observar as alterações em um dicionário, mas, por algum motivo, o retorno de chamada do relógio não é chamado.

Aqui está um controlador que eu uso:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    $scope.$watch('form', function(newVal, oldVal){
        console.log('changed');
    });
}

Aqui está o violino .

Espero que o retorno de chamada $ watch seja acionado sempre que o nome ou sobrenome for alterado, mas isso não acontece.

Qual é a maneira correta de fazer isso?


Respostas:


570

Ligue $watchcom trueo terceiro argumento:

$scope.$watch('form', function(newVal, oldVal){
    console.log('changed');
}, true);

Por padrão, ao comparar dois objetos complexos em JavaScript, eles serão verificados quanto à igualdade de "referência", que pergunta se os dois objetos se referem à mesma coisa, em vez de igualdade de "valor", que verifica se os valores de todas as propriedades daquelas objetos são iguais.

De acordo com a documentação angular , o terceiro parâmetro é para objectEquality:

Quando objectEquality == true, a desigualdade da watchExpression é determinada de acordo com a angular.equalsfunção. Para salvar o valor do objeto para comparação posterior, a angular.copyfunção é usada. Portanto, isso significa que a observação de objetos complexos terá implicações adversas na memória e no desempenho.


2
Esta é uma boa resposta, mas eu imagino que há momentos em que você não gostaria de assistir de forma recursiva um objeto
— Jason

16
Tem. Por padrão, se você não passar true, ele verificará a igualdade de referência se o valor monitorado for um objeto ou matriz. Nesse caso, você deve especificar as propriedades de forma explícita, como $scope.$watch('form.name')e$scope.$watch('form.surname')
— rossipedia

3
Obrigado. Foi exatamente o que eu perdi. Jason, você está certo - você nem sempre quer objetos recursivos assistindo, mas no meu caso, era exatamente o que eu precisava.
— Vladimir Sidorenko

1
O JS imutável e a comparação de referência podem ajudar a aumentar o desempenho.
— Dragos Rusu

13

O formobjeto não está sendo alterado, apenas a namepropriedade é

violino atualizado

function MyController($scope) {
$scope.form = {
    name: 'my name',
}

$scope.changeCount = 0;
$scope.$watch('form.name', function(newVal, oldVal){
    console.log('changed');
    $scope.changeCount++;
});
}

12

Pouca dica de desempenho se alguém tiver um tipo de serviço de armazenamento de dados com pares chave -> valor:

Se você tiver um serviço chamado dataStore , poderá atualizar um carimbo de data / hora sempre que seu objeto de big data for alterado. Dessa forma, em vez de observar profundamente o objeto inteiro, você está observando apenas um carimbo de data e hora para alterações.

app.factory('dataStore', function () {

    var store = { data: [], change: [] };

    // when storing the data, updating the timestamp
    store.setData = function(key, data){
        store.data[key] = data;
        store.setChange(key);
    }

    // get the change to watch
    store.getChange = function(key){
        return store.change[key];
    }

    // set the change
    store.setChange = function(key){
        store.change[key] = new Date().getTime();
    }

});

E em uma diretiva, você está apenas observando o carimbo de data e hora para mudar

app.directive("myDir", function ($scope, dataStore) {
    $scope.dataStore = dataStore;
    $scope.$watch('dataStore.getChange("myKey")', function(newVal, oldVal){
        if(newVal !== oldVal && newVal){
            // Data changed
        }
    });
});

1
Acho que vale a pena mencionar que, nesse caso, você perderia a funcionalidade de ter acesso ao valor antigo dos dados que está armazenando. newVal, oldValneste exemplo, os valores de carimbo de data e hora e não os valores de objetos observados. Não invalida esta resposta, apenas acho que é uma desvantagem que vale a pena mencionar.
— Michał Schielmann 14/11

É verdade que esse método é usado principalmente quando quero carregar uma estrutura de dados complicada como fonte de alguma coisa (uma nova versão de alguns dados, por exemplo). Se eu me preocupo com valores novos e antigos, não os armazenaria em objetos grandes e complicados; em vez disso, eu teria uma pequena representação de texto daquilo com o qual me preocupo e observo isso para alterar ou obtê-lo quando o carimbo de data e hora muda.
— Ferenc Takacs

5

O motivo pelo qual seu código não funciona é porque, $watchpor padrão, a verificação de referência. Então, em poucas palavras, certifique-se de que o objeto que é passado para ele seja um novo objeto. Mas no seu caso, você está apenas modificando alguma propriedade do objeto de formulário e não criando uma nova. Para fazê-lo funcionar, você pode passar truecomo o terceiro parâmetro.

$scope.$watch('form', function(newVal, oldVal){
    console.log('invoked');
}, true);

Funcionará, mas você pode usar $ watchCollection, que será mais eficiente do que $ watch, porque $watchCollectionobservará propriedades rasas no objeto de formulário. Por exemplo

$scope.$watchCollection('form', function (newVal, oldVal) {
    console.log(newVal, oldVal);
});

4

Enquanto você procura alterações no objeto de formulário, a melhor abordagem de observação é usar
$watchCollection. Consulte a documentação oficial para diferentes características de desempenho.


1

Tente o seguinte:

function MyController($scope) {
    $scope.form = {
        name: 'my name',
        surname: 'surname'
    }

    function track(newValue, oldValue, scope) {
        console.log('changed');
    };

    $scope.$watch('form.name', track);
}

0

Para quem deseja observar uma alteração em um objeto em uma variedade de objetos, isso pareceu funcionar para mim (como as outras soluções nesta página não):

function MyController($scope) {

    $scope.array = [
        data1: {
            name: 'name',
            surname: 'surname'
        },
        data2: {
            name: 'name',
            surname: 'surname'
        },
    ]


    $scope.$watch(function() {
        return $scope.data,
    function(newVal, oldVal){
        console.log(newVal, oldVal);
    }, true);

-3

você deve alterar em $ watch ....

function MyController($scope) {
    $scope.form = {
        name: 'my name',
    }

    $scope.$watch('form.name', function(newVal, oldVal){
        console.log('changed');
     
    });
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app>
    <div ng-controller="MyController">
        <label>Name:</label> <input type="text" ng-model="form.name"/>
            
        <pre>
            {{ form }}
        </pre>
    </div>
</div>


8
Respondendo a uma pergunta de dois anos com uma resposta que é uma duplicata quase exata de uma existente? Não é legal.
— Jared Smith
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.