Смотреть несколько атрибутов $ scope


442

Есть ли способ подписаться на события на нескольких объектах, используя $watch

Например

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

Ответы:


586

Начиная с 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 
});

7
в чем разница с $ watchCollection ('[a, b]') ??
Камиль Томшик

28
Разница в том, что вы можете использовать правильный массив вместо строки, которая выглядит как массив
Paolo Moretti

2
У меня была похожая проблема, но с использованием шаблона controllerAs. В моем случае исправление состояло в добавлении к переменным имени контроллера:$scope.$watchGroup(['mycustomctrl.foo', 'mycustomctrl.bar'],...
сморчки

1
Это не работает для меня на Angular 1.6. Если я добавлю консольный журнал в функцию, то увижу, что он запускается один раз с newValuesи oldValuesкак undefined, а индивидуальный просмотр свойств работает как обычно.
Алехандро Гарсия Иглесиас

290

Начиная с 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
});

Пример плунжера здесь

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


109
Обратите внимание, что первый параметр не является массивом. Это строка, которая выглядит как массив.
МК Сафи

1
Спасибо за это. если это сработает для меня, я дам тебе тысячу золотых монет - виртуальных, конечно :)
AdityaSaxena

5
Чтобы быть понятным (потребовалось несколько минут, чтобы понять) $watchCollection, предназначено для передачи объекта и обеспечения отслеживания изменений любых свойств объекта, в то время $watchGroupкак предназначено для передачи массива отдельных свойств для отслеживания изменений. Немного отличается, чтобы решать похожие, но разные проблемы. Уф!
Mattygabe

1
Так или иначе, newValues ​​и oldValues ​​всегда равны при использовании этого метода: plnkr.co/edit/SwwdpQcNf78pQ80hhXMr
mostruash

Спасибо. Это решило мою проблему. Есть ли какие-либо проблемы с производительностью при использовании $ watch?
Сайед Насир Аббас

118

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

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

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

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

8
Это похоже на вариант использования, который требует включения в каркас по умолчанию. Даже такое вычисляемое свойство, как простое, fullName = firstName + " " + lastNameтребует передачи функции в качестве первого аргумента (а не простого выражения типа 'firstName, lastName'). Angular настолько мощен, но есть некоторые области, где он может быть гораздо более дружественным к разработчикам способами, которые (как представляется) не ставят под угрозу производительность или принципы проектирования.
XML

4
Ну, $ watch просто принимает угловое выражение, которое оценивается по объему вызова. Так что вы могли бы сделать $scope.$watch('firstName + lastName', function() {...}). Вы можете просто сделать два часы: function nameChanged() {}; $scope.$watch('firstName', nameChanged); $scope.$watch('lastName', nameChanged);. Я добавил простой случай к ответу.
Эндрю Джослин

Плюсом в 'firstName + lastName' является конкатенация строк. Таким образом, angular просто проверяет, отличается ли результат конкатенации сейчас.
mb21

16
Конкатенация строк требует небольшой осторожности - если вы измените «paran orman» на «para norman», вы не вызовете ответ; лучше всего ставить разделитель типа "\ t | \ t" между первым и вторым слагаемым или просто придерживаться окончательного JSON
Дейв Эдельхарт,

хотя amberjs отстой, но в него встроена эта функция! услышать это угловатые ребята. Я думаю, что делать с часами - это самый рациональный способ.
Aladdin Mhemed

70

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

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

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

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

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


У меня был похожий вопрос. И это правильный ответ для меня.
Кельвин Ченг

Оба имеют небольшое снижение производительности, если элементы в выражении массива не являются простыми типами.
ноль

Это правда .. он делает полное сравнение глубины составляющих объектов. Который может или не может быть тем, что вы хотите. Посмотрите одобренный в настоящее время ответ для версии, использующей современный угловой, который не делает полное сравнение глубины.
Карен Зиллес

По какой-то причине, когда я пытался использовать $ watchCollection над массивом, я получил эту ошибку, TypeError: Object #<Object> has no method '$watchCollection'но это решение помогает мне решить мою проблему!
abottoni

Круто, вы даже можете наблюдать значения внутри объектов:$scope.$watch('[chaptercontent.Id, anchorid]', function (newValues, oldValues) { ... }, true);
Серж ван ден Овер

15

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

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

1
Почему вы используете _this? Разве область не распространяется на функции без явного ее определения?
sg.cc

1
Я использую машинопись, представленный код является скомпилированным результатом.
Ян Чжан

12

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

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

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


В этом случае log () будет выполняться несколько раз, верно? Я думаю, что в случае вложенных функций $ watch это не так. И это не должно быть в решении для просмотра нескольких атрибутов одновременно.
Джон Доу

Нет, журнал выполняется только один раз за изменение для каждого отслеживаемого свойства. Почему он вызывается несколько раз?
Эрик Эйгнер

Правильно, я имею в виду, что это не сработает, если мы хотим смотреть их все одновременно.
Джон Доу

1
Конечно, почему нет? Я делаю это таким образом в моем проекте. changed()вызывается всякий раз, когда что-то изменяется в любом из свойств. Это точно такое же поведение, как если бы была предоставлена ​​функция для комбинированного значения.
Эрик Эйгнер

1
Это немного отличается тем, что, если несколько элементов в массиве изменяются одновременно, $ watch будет запускать обработчик только один раз, а не один раз для каждого измененного элемента.
щипки

11

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

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

или

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

5

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


4

как насчет:

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

2
Без третьего trueпараметра to scope.$watch(как в вашем примере) он listener()будет срабатывать каждый раз, потому что анонимный хэш имеет разную ссылку каждый раз.
Петр В. Мёрч,

Я нашел это решение работает для моей ситуации. Для меня это было больше похоже на : return { a: functionA(), b: functionB(), ... }. Затем я проверяю возвращенные объекты новых и старых значений друг против друга.
RevNoah

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

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


2
Также убедитесь, что в этом случае не используйте аргументы newValue и oldValue, которые angular передает в обратный вызов, потому что они будут являться результатом конкатенации, а не только измененным полем
Akash Shinde

1

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]);
});
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.