Angular ngGrid watch for filtered row changes

I have a grid and a chart.  When the user changes the grid either via a filter or a sort, I want the chart to automatically update.  Aside from writing a "converter" to convert the model data from the grid format to the chart format, I needed to get my "watch" right.  Get it wrong and you will get this error:


Error: 10 $digest() iterations reached. Aborting!


I don't like that error.  That means something is happening recursively, like your watch is triggering a function that changes the model which you are watching and the watch fires again!  Recursive nightmare!

Skip ahead to the EDIT section.

OK, aside from that, what do you have to watch on an ngGrid to see if it "changed".  Well, the $watch method allows you to pass a string name of the model object to watch, or you can pass in a function!  Watching a huge object is time consuming!  It's better to watch a number or a string!  (Angular will be comparing the "old" and "new" values quite a lot.  If the comparison is easy, your app will be faster).

So I implemented this code to watch the ngGrid filteredRows object and a few others to see if the filtered data in the grid has changed.  This works great.  The one thing I don't like about it is that I am dependent on these variables in the ngGrid.  What if in the future the variable names changed?  I would much prefer a built in method to see if something has changed, but maybe in the future.  Here's the code.  Leave a comment if you think there is a better way!



$scope.gridChangedIndicator = function(scope) {
var watchString = "";
try {
watchString += scope.gridOptions.ngGrid.filteredRows.length;
watchString += scope.gridOptions.ngGrid.lastSortedColumns[0].field;
watchString += scope.gridOptions.ngGrid.lastSortedColumns[0].sortDirection;
} catch (err) {}
return watchString;
}

// When the grid data changes, the chart needs to change too.
$scope.$watch($scope.gridChangedIndicator,$scope.refreshChart);

===================================================================

EDIT:

This code was killing me.  This watch was sending angular into infinite-watch-nightmare-evil-glare.  I found out that using watch in a controller can be dangerous.  So I changed the above code to this:

$scope.$on('ngGridEventRows', $scope.refreshChart);

This event gets fired a little more often than I want, but my chart refresh is really fast.  I'd rather have it fire a few extra times than a million extra times.  This documentation about ngGrid Events is really helpful.  As of this moment, I think the events are a little lacking, and some do not seem to fire at the correct time for me.  (For example, the ngGridEventFilter event fires before the filter is really done.  ngGridEventSorted fires after which seems more useful.

EDIT 2:

OK, I'm still getting CPU burning infinite loops.  It all seems to be within the angular digest looping.  I wish I knew why this was, but I've burned up enough hours trying to do this.  I simply added a button to update the chart.  That only took 2 minutes because Angular is awesome!  PLEASE If anyone else is doing this effectively out there in the great internet, leave a comment to tell me how you did it.

EDIT 3:
This is all in flux right now.  The issue is still happening even decoupling the chart from the grid.  In a way that is good news, but now I need to figure out what is killing the page.

EDIT 4:
Enough edits already?  I was being hit by a double whammy and it exhibits itself in the same code ($digest).  Indeed, when I was adding a $watch more than once in my controller, it was causing really bad performance in $digest.  But also, Angular grid has an issue clearing the search filter.  It goes nuts in the $digest.  It is issue 777 on github.

Tomorrow for EDIT 5, I'll put the grid and chart back together and simply disable filtering on my ngGrid to see if that will solve it.  Good night!

Conclusion

ngGrid has a bad issue when the search filter is cleared out.  See issue 777 on github.  That was causing issues in $digest.  But I also had a call to $scope.$watch that was happening more than once in my controller, which is also bad and causes a very similar issue.  It turns out the $on code above is called quite often... more than necessary.  So I reverted back to the $watch code instead.

Comments

Popular Posts