Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS ignoring a key in an watched object or overriding the $watch listener

I'm deep watching a property that is bound to multiple controls:

$scope.$watch('config', function(){}, true);

the config itself contains various parameters:

  • scale
  • point
  • aggregates
  • current

I want to ignore changes to scale when it is changed by a specific control and a specific function.

Is there a way to ignore a specific property or override the watch is specific cases?


For now this is what i'm doing:

The dataChange now fires only on certain changes, in this case when other properties, not zoom are changing.

In order to disable the dataChange for a specific zoom case i just assigned it to the rest of the cases.

I'm using Switch and not if/else just because it's more descriptive and easily extendable for more cases.

  $scope.$watch('config', function(n,o,scope){
   $scope.config = n;
    if (n != o) {
     switch(true){
      case n.zoom != o.zoom:
      break;

      default:
         $scope.dataChange($scope.dataTable);              
      };
    }
}, true);
like image 405
BarakChamo Avatar asked May 16 '13 13:05

BarakChamo


2 Answers

I don't like any of these answers. The first parameter of $watch is what to watch, which accepts the property name as a string, or a function to return the value. Simply use a function & return the value you want to watch. Here I use lodash JS library to $watch a new object that is based on the real object, but with the property stripped:

$scope.$watch(function() {
  return _.omit($scope.config, 'scale');
}, function(oldVal, newVal) {
  console.log(oldVal, newVal);
});

Without Lodash [blacklist properties]:

$scope.$watch(function() {
  var myConfig = Angular.copy(config);
  delete myConfig.scale;
  return myConfig;
}, function(oldVal, newVal) {
  console.log(oldVal, newVal);
});

Without Lodash [whitelist properties]:

$scope.$watch(function() {
  return {
     point: $scope.config.point,
     aggregates: $scope.config.aggregates,
     current: $scope.config.current
  };
}, function(oldVal, newVal) {
  console.log(oldVal, newVal);
});

In my opinion the other answers are not the "Angular way". This approach is not only more succinct than the other answers which are messy, but also avoids performing a redundant object comparison when the $watch fires. Keep in mind the other answers incur the cost of object comparison twice, once for the $watch itself in the Angular code, then you incur the cost of your "home made" object comparison in your callback function. My approach ensures the object comparison is only incurred once, in the Angular code, by stripping the unwanted property before feeding the object into the $watch for comparison.

like image 172
Josh Ribakoff Avatar answered Oct 06 '22 21:10

Josh Ribakoff


Not as far as I know, but a simple check would do the trick:

$scope.$watch('config', function(newValue, oldValue){
  if (newValue.scale == oldValue.scale) {
    // ignore this
    return;
  }
  // continue...
}, true);
like image 2
jlb Avatar answered Oct 06 '22 23:10

jlb