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

Ответы:

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
  });
guyk
источник
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...);
wacamo
источник
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]);
});
Винит Соланки
источник