Assista a vários atributos de escopo $


Respostas:


585

A partir do AngularJS 1.3, existe um novo método chamado $watchGrouppara observar um conjunto de expressões.

$scope.foo = 'foo';
$scope.bar = 'bar';

$scope.$watchGroup(['foo', 'bar'], function(newValues, oldValues, scope) {
  // newValues array contains the current values of the watch expressions
  // with the indexes matching those of the watchExpression array
  // i.e.
  // newValues[0] -> $scope.foo 
  // and 
  // newValues[1] -> $scope.bar 
});

7
qual é a diferença contra $ watchCollection ('[a, b]') ??
Kamil Tomšík

28
A diferença é que você pode usar uma matriz adequada, em vez de uma string que se parece com um array
Paolo Moretti

2
Eu tive um problema semelhante, mas usando o padrão controllerAs. No meu caso a correção consistiu em prepend às variáveis o nome do controlador:$scope.$watchGroup(['mycustomctrl.foo', 'mycustomctrl.bar'],...
cogumelos

1
Isso não parece funcionar para mim no Angular 1.6. Se eu colocar um log do console na função, posso ver que ele é executado apenas uma vez com newValuese oldValuescomo undefined, enquanto observa individualmente as propriedades funcionar normalmente.
Alejandro García Iglesias

290

A partir do AngularJS 1.1.4, você pode usar $watchCollection:

$scope.$watchCollection('[item1, item2]', function(newValues, oldValues){
    // do stuff here
    // newValues and oldValues contain the new and respectively old value
    // of the observed collection array
});

Exemplo de Plunker aqui

Documentação aqui


109
Observe que o primeiro parâmetro não é uma matriz. É uma string que se parece com uma matriz.
MK Safi

1
obrigado por isso. se ele funciona para mim, vou dar-lhe mil moedas de ouro - os virtuais, é claro :)
AdityaSaxena

5
Para ser claro (levei alguns minutos para perceber), o $watchCollectionobjetivo é entregar um objeto e fornecer um controle de alterações em qualquer uma das propriedades do objeto, enquanto que o $watchGroupobjetivo é fornecer uma variedade de propriedades individuais para observar alterações. Um pouco diferente para lidar com problemas semelhantes, mas diferentes. Ufa!
Mattygabe

1
De alguma forma, NewValues e oldValues são sempre iguais quando utilizar este método: plnkr.co/edit/SwwdpQcNf78pQ80hhXMr
mostruash

Obrigado. Isso resolveu meu problema. Existe algum problema / problema no desempenho do uso do $ watch?
Syed Nasir Abbas

118

$watch O primeiro parâmetro também pode ser uma função.

$scope.$watch(function watchBothItems() {
  return itemsCombinedValue();
}, function whenItemsChange() {
  //stuff
});

Se seus dois valores combinados forem simples, o primeiro parâmetro é apenas uma expressão angular normalmente. Por exemplo, nome e sobrenome:

$scope.$watch('firstName + lastName', function() {
  //stuff
});

8
Parece um caso de uso que exige inclusão na estrutura por padrão. Mesmo uma propriedade computada tão simples quanto fullName = firstName + " " + lastNamerequer passar uma função como o primeiro argumento (em vez de uma expressão simples como 'firstName, lastName'). O Angular é TÃO poderoso, mas há algumas áreas em que pode ser muito mais amigável ao desenvolvedor de maneiras que não parecem comprometer os princípios de desempenho ou design.
XML

4
Bem, $ watch apenas usa uma expressão angular, que é avaliada no escopo em que é chamada. Então você poderia fazer $scope.$watch('firstName + lastName', function() {...}). Você também pode apenas fazer dois relógios: function nameChanged() {}; $scope.$watch('firstName', nameChanged); $scope.$watch('lastName', nameChanged);. Eu adicionei o caso simples à resposta.
Andrew Joslin

O sinal de mais em 'firstName + lastName' é concatenação de string. Tão angular apenas verifica se o resultado da concatenação é diferente agora.
Mb21

16
A concatenação de cadeias requer um pouco de cuidado - se você alterar "paran orman" para "para norman", não será acionada uma resposta; melhor colocar um divisor como "\ t | \ t" entre o primeiro eo segundo mandato, ou apenas furar a JSON que é definitiva
Dave Edelhart

embora amberjs seja péssimo, mas ele possui esse recurso! ouvir aqueles caras angulares. Eu acho que fazer relógios é a maneira mais racional possível.
Aladdin Mhemed

70

Aqui está uma solução muito semelhante ao seu pseudo-código original que realmente funciona:

$scope.$watch('[item1, item2] | json', function () { });

EDIT: Ok, acho que isso é ainda melhor:

$scope.$watch('[item1, item2]', function () { }, true);

Basicamente, estamos pulando a etapa json, que parecia idiota para começar, mas não estava funcionando sem ela. A chave é o terceiro parâmetro frequentemente omitido, que ativa a igualdade de objetos em oposição à igualdade de referência. Em seguida, as comparações entre nossos objetos de matriz criados realmente funcionam corretamente.


Eu tive uma pergunta semelhante. E esta é a resposta correta para mim.
Calvin Cheng

Ambos têm uma pequena sobrecarga de desempenho se os itens na expressão da matriz não forem tipos simples.
Nulo

Isso é verdade ... está fazendo uma comparação completa dos objetos componentes. Que pode ou não ser o que você deseja. Veja a resposta atualmente aprovada para uma versão usando o angular moderno que não faz uma comparação completa.
Karen Zilles

Por alguma razão, quando estava tentando usar o $ watchCollection em uma matriz, recebi esse erro, TypeError: Object #<Object> has no method '$watchCollection'mas esta solução me ajuda a resolver meu problema!
abottoni

Legal, você pode até observar valores dentro dos objetos:$scope.$watch('[chaptercontent.Id, anchorid]', function (newValues, oldValues) { ... }, true);
Serge van den Oever

15

Você pode usar funções no $ watchGroup para selecionar os campos de um objeto no escopo.

        $scope.$watchGroup(
        [function () { return _this.$scope.ViewModel.Monitor1Scale; },   
         function () { return _this.$scope.ViewModel.Monitor2Scale; }],  
         function (newVal, oldVal, scope) 
         {
             if (newVal != oldVal) {
                 _this.updateMonitorScales();
             }
         });

1
Por que você usa _this? O escopo não é carregado para as funções sem defini-lo explicitamente?
Sg.cc 21/05

1
Eu uso o texto datilografado, o código apresentado é o resultado compilado.
Yang Zhang

12

Por que não simplesmente envolvê-lo em um forEach?

angular.forEach(['a', 'b', 'c'], function (key) {
  scope.$watch(key, function (v) {
    changed();
  });
});

Trata-se da mesma sobrecarga de fornecer uma função para o valor combinado, sem precisar se preocupar com a composição do valor .


Nesse caso, log () seria executado várias vezes, certo? Eu acho que, no caso de funções $ watch aninhadas, isso não aconteceria. E não deve estar na solução para assistir a vários atributos ao mesmo tempo.
John Doe

Não, o log é executado apenas uma vez por alteração por propriedade monitorada. Por que seria invocado várias vezes?
Erik Aigner

Certo, quero dizer que não funcionaria bem se quiséssemos assistir todos eles simultaneamente.
John Doe

1
Claro, por que não? Eu faço dessa maneira em um projeto meu. changed()é chamado sempre que algo muda em qualquer uma das propriedades. Esse é exatamente o mesmo comportamento como se uma função para o valor combinado fosse fornecida.
Erik Aigner

1
É um pouco diferente, pois se vários itens da matriz forem alterados ao mesmo tempo, o $ watch disparará o manipulador apenas uma vez em vez de uma vez por item alterado.
Bingles

11

Uma solução um pouco mais segura para combinar valores pode ser o seguinte como sua $watchfunção:

function() { return angular.toJson([item1, item2]) }

ou

$scope.$watch(
  function() {
    return angular.toJson([item1, item2]);
  },
  function() {
    // Stuff to do after either value changes
  });

5

O primeiro parâmetro $ watch pode ser expressão ou função angular . Veja a documentação sobre $ scope. $ Watch . Ele contém muitas informações úteis sobre como o método $ watch funciona: quando watchExpression é chamado, como angular compara resultados, etc.


4

e quanto a:

scope.$watch(function() { 
   return { 
      a: thing-one, 
      b: thing-two, 
      c: red-fish, 
      d: blue-fish 
   }; 
}, listener...);

2
Sem o terceiro trueparâmetro para scope.$watch(como no seu exemplo), listener()seria acionado toda vez, porque o hash anônimo tem uma referência diferente a cada vez.
Peter V. Mørch 27/01

Eu encontrei esta solução funcionou para a minha situação. Para mim, era mais como: return { a: functionA(), b: functionB(), ... }. Em seguida, verifico os objetos retornados dos valores novos e antigos.
RevNoah

4
$scope.$watch('age + name', function () {
  //called when name or age changed
});

Aqui, a função será chamada quando os valores de idade e nome forem alterados.


2
Também não se esqueça, neste caso, não usar os argumentos NewValue e oldValue que passes angulares para o retorno de chamada, porque eles vão ser a concatenação resultante e não apenas o campo que foi alterado
Akash Shinde

1

O Angular introduzido $watchGroupna versão 1.3, usando o qual podemos observar várias variáveis, com um único $watchGroupbloco $watchGroupassume o array como primeiro parâmetro no qual podemos incluir todas as nossas variáveis ​​a serem observadas.

$scope.$watchGroup(['var1','var2'],function(newVals,oldVals){
   console.log("new value of var1 = " newVals[0]);
   console.log("new value of var2 = " newVals[1]);
   console.log("old value of var1 = " oldVals[0]);
   console.log("old value of var2 = " oldVals[1]);
});
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.