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.