12

I do have two $scope variables. They are called $scope.image and $scope.titleimage.

Basically the store the same type of contents. I want to track now when either one of them gets updated. But so far I could not figure out how to have two variables tracked in a single $scope.$watch() callback.

// How can I watch titleimage here as well?
$scope.$watch('image', function(media) {
    console.log('Media change discoverd!');
}); 
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
powtac
  • 40,542
  • 28
  • 115
  • 170
  • 1
    You can create a new object on your scope that has these two properties and then watch the parent object. Such as `$scope.o={image: scope.image, titleImage: scope.titleImage};` – Chandermani Jul 09 '13 at 14:41
  • possible duplicate of [Can I combine watching of multiple fields into one watch with AngularJS?](http://stackoverflow.com/questions/17872919/can-i-combine-watching-of-multiple-fields-into-one-watch-with-angularjs) --- please see **dluz**'s answer and see the **last example** of how to do that by `watchCollection`. – quetzalcoatl Aug 28 '14 at 11:32
  • 1
    actually, I've just found even better one: `watchGroup` see http://stackoverflow.com/questions/11952579/watch-multiple-scope-attributes-in-angularjs – quetzalcoatl Aug 28 '14 at 11:35

4 Answers4

32

$watch method accepts a function as first parameter (beside a string). $watch will "observe" the return value of the function and call the $watch listener if return value is changed.

$scope.$watch(
  function(scope){
    return {image: scope.image, titleImage: scope.titleImage};
  }, 
  function(images, oldImages) {
    if(oldImages.image !== images.image){
      console.log('Image changed');
    }
    if(oldImages.titleImage !== images.titleImage){
      console.log('titleImage changed');
    }
  }, 
  true
); 

Also you might observe a concatenated value, but that doesn't let you know which one of the observed values actually changed:

$scope.$watch('image + titleImage',
  function(newVal, oldVal) {
    console.log('One of the images have changed');
  }
); 

And you can also watch an array of scope variables:

$scope.$watch('[image, titleImage]',
  function(images, oldImages) {
    if(oldImages[0] !== images[0]){
      console.log('Image changed');
    }
    if(oldImages[1] !== oldImages[1]){
      console.log('titleImage changed');
    }
  },
  true
); 
jaredwilli
  • 11,762
  • 6
  • 42
  • 41
Stewie
  • 60,366
  • 20
  • 146
  • 113
16

Stewie's suggestions will work. But there are a thousand ways to skin this cat. I'd submit that if you're watching two separate values, there's nothing wrong with setting up two watches for them with a shared function between them:

functional programming to the rescue!

Using functions to create functions is awesome.

function logChange(expr) {
   return function(newVal, oldVal) {
      console.log(expr+ ' has changed from ' + oldVal + ' to ' + newVal);
   };
}

$scope.$watch('image', logChange('image'));
$scope.$watch('titleImage', logChange('titleImage'));

OR... you could even wrap the watch setup in it's own function (much less exciting, IMO):

function logChanges(expr) {
   $scope.$watch(expr, function(newVal, oldVal) {
      console.log(expr+ ' has changed from ' + oldVal + ' to ' + newVal);
   });
};

logChanges('image');
logChanges('titleImage');

.. but I have a thousand of them, you say?

//assuming the second function above
angular.forEach(['image', 'titleimage', 'hockeypuck', 'kitchensink'], logChanges);
Community
  • 1
  • 1
Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
0

Use computed and return multiple variables in an array that you want to listen to that should execute the same function.

computed: {
  photo () {
    return [this.image, this.title]
  }
},
watch: {
  photo () {
    console.log('changed')
  }
},
EnzoTrompeneers
  • 1,031
  • 1
  • 14
  • 30
0

The correct answer was found here:

https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollection

"for arrays, this implies watching the array items; for object maps, this implies watching the properties"

Example:

$scope.$watchCollection(function () {
    return {t : ctrl.time, d : ctrl.date};
}, function(value) {
    ctrl.updateDateTime();
});
Marek Manduch
  • 2,303
  • 1
  • 19
  • 22