How to control $scope.$watch iteration?

168 Views Asked by At

I am trying to control the $scope.$watch function. Currently, I am having an infinite loop on my iteration.

$scope.$watch('dashboards', function(newVal, oldVal) {
    if (newVal !== oldVal) {
        $scope.dashboard = $localStorage.sample;
        $scope.dashboards['1'].id = $scope.dashboards[1].id + 1;
        $localStorage.sample = $scope.dashboards[1];
        console.log('newval: '+$scope.dashboards['1'].id);
    } else {
        $scope.$storage = $localStorage;
        $localStorage.sample = $scope.dashboards[1];
        $scope.dashboard = $localStorage.sample;
        $scope.dashboards['1'].id = $scope.dashboards[1].id.length + 1;
        $localStorage.sample = $scope.dashboards[1];
    console.log('oldval: '+$scope.dashboards['1'].id);
    }
}, true);

Here's my scope.dashboards:

$scope.dashboards = {
        '1' : {
            id : '1',
            widgets : [{
                code : "bla1.html",
                col : 0,
                row : 0,
                sizeY : 1,
                sizeX : 2,
                title : "Bla1"
            }, {
                code : "bla2.html",
                col : 2,
                row : 0,
                sizeY : 2,
                sizeX : 2,
                title : "Bla2"
            }, {
                code : "bla3.html",
                col : 0,
                row : 4,
                sizeY : 1.5,
                sizeX : 2,
                title : "Bla3"
            }
}

What I am trying to accomplish is that everytime there is a change in the position of widgets inside my $scope.dashboards I will store a new ID in thhe localStorage to save its positions state so as when the browser is closed/refresh it will still have its session/positions retained. Maybe you got an idea on how will I be able to work on this? Thank you!

2

There are 2 best solutions below

0
On

A rough outline of an approach you can use is as follows:

var modificationInProgress = false;
$scope.$watch('dashboards', function(newVal, oldVal) {
  if (!modificationInProgress) {
    modificationInProgress = true;
    // modify dashboards
  }
  $timeout(function() {
    modificationInProgress = false;
  }, 0);
});

If you're expecting dashboard to be modified multiple times in the same tick, this might cause some interesting bugs. But, if you're only expecting dashboards to be modified once per tick (say, when a user clicks a button), this should work.

But - your code is odd. I think that refactoring it to avoid watching and modifying the same variable is probably your best move. For example, you might want to watch a different object and store the number of times they were moved on a separate object, avoiding the infinite loop problem.

0
On

The root of the problem is that by assigning a different value in the $watcher (of $scope.dashboards) to the value being $watched, you're causing an infinite loop.

You should either avoid using the $watch as the trigger to update the $scope.dashboards.id and use the actual trigger that caused the change in the widgets. (Off-topic, but the use of the property id is slightly awkward - id has a very specific meaning, I would change it to "version" or something along those lines)

Or, the other option is to $watch only the widgets property, instead of a deep-watch of dashboards.

$scope.$watch("dashboards['1'].widgets", function(newVal, oldVal){
 ...
}, true);