Наблюдайте за несколькими атрибутами $ scope

Есть ли способ подписаться на события нескольких объектов с помощью $watch

E.g.

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

person Greg    schedule 14.08.2012    source источник


Ответы (11)


Начиная с AngularJS 1.3 появился новый метод под названием $watchGroup для наблюдения за набором выражений. .

$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 
});
person Paolo Moretti    schedule 06.05.2014
comment
в чем разница с $ watchCollection ('[a, b]') ?? - person Kamil Tomšík; 12.01.2015
comment
Разница в том, что вы можете использовать правильный массив вместо строки, которая выглядит как массив. - person Paolo Moretti; 25.02.2015
comment
У меня была аналогичная проблема, но с использованием шаблона controllerAs. В моем случае исправление заключалось в добавлении к переменным имени контроллера: $scope.$watchGroup(['mycustomctrl.foo', 'mycustomctrl.bar'],... - person morels; 15.10.2015
comment
Похоже, это не работает для меня на Angular 1.6. Если я помещу консольный журнал для функции, я вижу, что она запускается только один раз с newValues и oldValues как undefined, при этом индивидуально наблюдая, как свойства работают как обычно. - person Alejandro García Iglesias; 03.04.2017

Начиная с AngularJS 1.1.4 вы можете использовать $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
});

Пример Plunker здесь

Документация здесь

person Răzvan Flavius Panda    schedule 20.06.2013
comment
Обратите внимание, что первый параметр не является массивом. Это строка, похожая на массив. - person M.K. Safi; 22.09.2013
comment
Спасибо за это. если у меня сработает, дам тысячу золотых монет - виртуальных, конечно :) - person AdityaSaxena; 20.08.2014
comment
Для ясности (мне потребовалось несколько минут, чтобы понять) $watchCollection предназначен для передачи объекта и наблюдения за изменениями любых свойств объекта, тогда как $watchGroup предназначен для передачи массива отдельных свойств для отслеживания изменений в . Немного отличается, чтобы решать похожие, но разные проблемы. Уф! - person Mattygabe; 28.02.2015
comment
Почему-то newValues ​​и oldValues ​​всегда равны при использовании этого метода: plnkr.co/edit/SwwdpQcNf78pQ80hhXM - person mostruash; 04.05.2016
comment
Спасибо. Это решило мою проблему. Есть ли какие-либо проблемы с производительностью при использовании $ watch? - person Syed Nasir Abbas; 03.07.2017

$watch первый параметр также может быть функцией.

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

Если ваши два комбинированных значения просты, первый параметр обычно представляет собой просто угловое выражение. Например, firstName и lastName:

$scope.$watch('firstName + lastName', function() {
  //stuff
});
person Andrew Joslin    schedule 14.08.2012
comment
Это похоже на вариант использования, который по умолчанию требует включения в структуру. Даже такое простое вычисляемое свойство, как fullName = firstName + " " + lastName, требует передачи функции в качестве первого аргумента (а не простого выражения, такого как 'firstName, lastName'). Angular НАСТОЛЬКО мощный инструмент, но есть некоторые области, в которых он может быть намного более удобен для разработчиков, не влияя (кажется) на производительность или принципы проектирования. - person XML; 25.08.2012
comment
Что ж, $ watch просто принимает угловое выражение, которое оценивается в той области видимости, в которой оно вызвано. Итак, вы могли сделать $scope.$watch('firstName + lastName', function() {...}). Вы также можете просто сделать две часы: function nameChanged() {}; $scope.$watch('firstName', nameChanged); $scope.$watch('lastName', nameChanged);. Я добавил к ответу простой случай. - person Andrew Joslin; 25.08.2012
comment
Плюс в "firstName + lastName" - это конкатенация строк. Итак, angular просто проверяет, отличается ли результат конкатенации. - person mb21; 06.09.2012
comment
Конкатенация строк требует некоторой осторожности - если вы измените паранорман на пара норман, вы не вызовете ответа; лучше всего поставить разделитель типа \ t | \ t между первым и вторым членами или просто придерживаться JSON, который является окончательным - person Dave Edelhart; 07.10.2012
comment
хотя amberjs отстой, но в нем есть встроенная функция! слышите, что угловатые ребята. Я думаю, что делать с часами - самый рациональный и выполнимый способ. - person Aladdin Mhemed; 24.10.2012
comment
Это великолепно! Вы также можете переписать его как: $ scope. $ Watch (itemsCombinedValue, function whenItemsChange () {// stuff}); Где itemsCombined - это функция, возвращающая значение. Это немного читабельнее. - person Stone; 08.11.2012

Вот решение, очень похожее на ваш исходный псевдокод, который действительно работает:

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

РЕДАКТИРОВАТЬ: Хорошо, я думаю, это даже лучше:

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

По сути, мы пропускаем этап json, который изначально казался глупым, но без него он не работал. Ключевым моментом является часто опускаемый третий параметр, который включает равенство объектов, а не равенство ссылок. Тогда сравнения между нашими созданными объектами массива действительно работают правильно.

person Karen Zilles    schedule 19.01.2013
comment
У меня был аналогичный вопрос. И это для меня правильный ответ. - person Calvin Cheng; 31.05.2013
comment
У обоих есть небольшие накладные расходы на производительность, если элементы в выражении массива не являются простыми типами. - person null; 18.08.2013
comment
Это правда ... он выполняет полное сравнение компонентов компонентов. Что может быть, а может и не быть тем, что вы хотите. См. Одобренный в настоящее время ответ для версии, использующей современный angular, которая не выполняет полное сравнение глубины. - person Karen Zilles; 27.08.2013
comment
По какой-то причине, когда я пытался использовать $ watchCollection для массива, я получил эту ошибку TypeError: Object #<Object> has no method '$watchCollection', но это решение помогает мне решить мою проблему! - person abottoni; 10.09.2013
comment
Круто, вы даже можете наблюдать за значениями внутри объектов: $scope.$watch('[chaptercontent.Id, anchorid]', function (newValues, oldValues) { ... }, true); - person Serge van den Oever; 28.01.2014

Вы можете использовать функции в $ watchGroup для выбора полей объекта в области видимости.

        $scope.$watchGroup(
        [function () { return _this.$scope.ViewModel.Monitor1Scale; },   
         function () { return _this.$scope.ViewModel.Monitor2Scale; }],  
         function (newVal, oldVal, scope) 
         {
             if (newVal != oldVal) {
                 _this.updateMonitorScales();
             }
         });
person Yang Zhang    schedule 20.05.2015
comment
Почему вы используете _this? Разве область видимости не переносится в функции без ее явного определения? - person sg.cc; 21.05.2015
comment
Использую машинописный текст, представленный код - это скомпилированный результат. - person Yang Zhang; 22.05.2015

Почему бы просто не обернуть его forEach?

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

Это примерно такие же накладные расходы, как и предоставление функции для комбинированного значения, без необходимости беспокоиться о составе значений.

person Erik Aigner    schedule 12.05.2013
comment
В этом случае log () будет выполняться несколько раз, верно? Я думаю, что в случае вложенных функций $ watch это не так. И этого не должно быть в решении для просмотра сразу нескольких атрибутов. - person John Doe; 13.05.2013
comment
Нет, журнал выполняется только один раз при изменении каждого наблюдаемого свойства. Почему его нужно вызывать несколько раз? - person Erik Aigner; 13.05.2013
comment
Правильно, я имею в виду, что это не сработает, если мы захотим смотреть их всех одновременно. - person John Doe; 13.05.2013
comment
Конечно, почему бы и нет? Я так и поступаю в своем проекте. changed() вызывается всякий раз, когда что-то изменяется в любом из свойств. Это точно такое же поведение, как если бы была предоставлена ​​функция для комбинированного значения. - person Erik Aigner; 13.05.2013
comment
Это немного отличается тем, что если несколько элементов в массиве изменяются одновременно, $ watch запускает обработчик только один раз, а не один раз для каждого измененного элемента. - person bingles; 29.10.2014
comment
С помощью этого метода, если все элементы изменились за один тик, changed() будет вызываться три раза. С чем-то вроде $watchGroup (или написанием функции, которая объединяет разные значения в ключ), это будет вызываться только один раз. - person binki; 29.06.2018

Несколько более безопасным решением для объединения значений может быть использование следующей функции в качестве $watch:

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

or

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

Первый параметр $ watch может быть угловым выражением или функцией. См. Документацию по $ scope. $ Watch. Он содержит много полезной информации о том, как работает метод $ watch: когда вызывается watchExpression, как angular сравнивает результаты и т. Д.

person Artem Andreev    schedule 14.08.2012

как насчет:

scope.$watch(function() { 
   return { 
      a: thing-one, 
      b: thing-two, 
      c: red-fish, 
      d: blue-fish 
   }; 
}, listener...);
person wacamo    schedule 12.11.2012
comment
Без третьего параметра true до scope.$watch (как в вашем примере) listener() будет срабатывать каждый раз, потому что анонимный хэш каждый раз имеет другую ссылку. - person Peter V. Mørch; 27.01.2014
comment
Я обнаружил, что это решение работает в моей ситуации. Для меня это было больше похоже на: return { a: functionA(), b: functionB(), ... }. Затем я сравниваю возвращенные объекты нового и старого значений друг с другом. - person RevNoah; 18.03.2014

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

Здесь функция будет вызываться при изменении значения возраста и имени.

person Akash Shinde    schedule 16.04.2015
comment
Также убедитесь, что в этом случае не используйте аргументы newValue и oldValue, которые angular передает в обратный вызов, потому что они будут результирующей конкатенацией, а не только измененным полем. - person Akash Shinde; 16.04.2015

Angular представил $watchGroup в версии 1.3, с помощью которого мы можем наблюдать за несколькими переменными, с одним блоком $watchGroup, $watchGroup принимает массив в качестве первого параметра, в который мы можем включить все наши переменные для наблюдения.

$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]);
});
person Vinit Solanki    schedule 02.04.2018