Angular ui-grid / ng-grid filterChanged firing too often

I am trying to implement a grid in angular using server side sorting, pagination on server side and filtering on server side. Using ui-grid (unstable), I added ui.grid.paging and it worked. Very nice touch to the developers.

However .... $ scope.gridApi.core.on.filterChanged is triggered for each key pressed, so when I search for "Patrick" in the column of the given name, seven events are fired and seven receive requests are deleted on my server, Worse, since this is a large set that I am filtering, this operation is quite expensive, and the results even overtake me as the most specific filter that gets the fastest result, triggering success before the less specific result is processed.

I would like to slow it down, for example, "the fire stopped after entering." I have a lot of new in javascript and REST, this is my first project in the real world. I would really appreciate any ideas on how to deal with this problem. This seems like a common scenario, so there may be some standard solutions or best practices that I am missing.

Regards, Patrick.

+6
source share
4 answers

If you want to go "all angular", I would suggest using $timeout inside the on.filterChanged event on.filterChanged :

 if (angular.isDefined($scope.filterTimeout)) { $timeout.cancel($scope.filterTimeout); } $scope.filterTimeout = $timeout(function () { getPage(); }, 500); 

where 500 is the time (in milliseconds) you want to wait between each on.filterChanged event before going to the server, and getPage () is a function that actually sends to the server and retrieves the data.

Remember to cancel the $timeout on the controller's destroy event:

 $scope.$on("$destroy", function (event) { if (angular.isDefined($scope.filterTimeout)) { $timeout.cancel($scope.filterTimeout); } }); 
+16
source

I was dealing with the same problem, and I came up with a different solution, which IMHO is more "Angular-friendly." I used the ngModelOptions directive introduced in Angular 1.3. I replaced the default uiGrid filter template ("ui-grid / ui-grid-filter") with a custom one and set the ngModelOptions directive to input with a default value of 300 ms and 0 ms for blur.

This is a sample template based on the original ui-grid 3.0.5 template, where I also changed the default CSS classes with Bootstrap classes:

 $templateCache.put('ui-grid/ui-grid-filter-custom', "<div class=\"ui-grid-filter-container\" ng-repeat=\"colFilter in col.filters\" ng-class=\"{'ui-grid-filter-cancel-button-hidden' : colFilter.disableCancelFilterButton === true }\">" + "<div ng-if=\"colFilter.type !== 'select'\"><input type=\"text\" class=\"input-sm form-control\" ng-model=\"colFilter.term\" ng-model-options=\"{ debounce : { 'default' : 300, 'blur' : 0 }}\" ng-attr-placeholder=\"{{colFilter.placeholder || ''}}\" aria-label=\"{{colFilter.ariaLabel || aria.defaultFilterLabel}}\"><div role=\"button\" class=\"ui-grid-filter-button\" ng-click=\"removeFilter(colFilter, $index)\" ng-if=\"!colFilter.disableCancelFilterButton\" ng-disabled=\"colFilter.term === undefined || colFilter.term === null || colFilter.term === ''\" ng-show=\"colFilter.term !== undefined && colFilter.term !== null && colFilter.term !== ''\"><i class=\"ui-grid-icon-cancel\" ui-grid-one-bind-aria-label=\"aria.removeFilter\">&nbsp;</i></div></div>" + "<div ng-if=\"colFilter.type === 'select'\"><select class=\"form-control input-sm\" ng-model=\"colFilter.term\" ng-attr-placeholder=\"{{colFilter.placeholder || aria.defaultFilterLabel}}\" aria-label=\"{{colFilter.ariaLabel || ''}}\" ng-options=\"option.value as option.label for option in colFilter.selectOptions\"><option value=\"\"></option></select><div role=\"button\" class=\"ui-grid-filter-button-select\" ng-click=\"removeFilter(colFilter, $index)\" ng-if=\"!colFilter.disableCancelFilterButton\" ng-disabled=\"colFilter.term === undefined || colFilter.term === null || colFilter.term === ''\" ng-show=\"colFilter.term !== undefined && colFilter.term != null\"><i class=\"ui-grid-icon-cancel\" ui-grid-one-bind-aria-label=\"aria.removeFilter\">&nbsp;</i></div></div>" + "</div>" ); 

The final step for this is to set this template in each column where you enable filtering:

 columnDefs: [{ name: 'theName', displayName: 'Whatever', filterHeaderTemplate: 'ui-grid/ui-grid-filter-custom' }] 

Unfortunately, I could not find a way to define this template globally, so I had to repeat the HeaderTemplate filter everywhere ... This is the only drawback, but on the other hand, you can also add additional filters to your custom template if you need.

Hope this helps!

+8
source

I would recommend you take a look at reactive extensions for Javascript . The jetbook is built specifically for these scenarios. There is a method called .Throttle (milliseconds) that you can connect to an observable that is called by the handler (to hit your API) after x milliseconds have passed so that the user does not enter anything.

Rx-Js and angular play well together

Here is an example from one of my projects doing something similar:

 observeOnScope($scope, 'tag', true) .throttle(1000) .subscribe(function (data) { if (data.newValue) { $http({ url: api.endpoint + 'tag/find', method: 'GET', params: {text: data.newValue} }).then(function (result) { $scope.candidateTags = result.data; }) } }); 

This code takes $scope.tag and turns it into observable. .throttle(1000) means that after 1 second from $scope.tag , without changing the subscription function, where (data) is called is the new value. After that you can click on your API.

I used this to do typeahead lookups of values ​​from my API, so striking the backend every time the message changed there was clearly no way :)

0
source

The JavaScript setTimeout function can also be used to provide a delay, as described below.

 $scope.gridApi.core.on.filterChanged( $scope, function() { if (angular.isDefined($scope.filterTimeout)) { clearTimeout($scope.filterTimeout); } $scope.filterTimeout = setTimeout(function(){ getPage(); },1000); }); 
0
source

Source: https://habr.com/ru/post/979134/


All Articles