5

I have a big list of data (4000+ items). When start typing - my browser freezes (up to 15 sec). So i need to turn off auto-filter feature, and bind filter function to the button click. Looking for answer via Google gave no results. How i can do this? Help me please :)

Code:

<input ng-model="search.phone" type="text" placeholder="Телефон...">
<input ng-model="search.name" type="text" placeholder="Имя...">
<input ng-model="search.city" type="text" placeholder="Город...">

<div ng-repeat="user in users | filter:search" class="user_block" ng-include src="userTemplate"></div>

and controller:

app.controller("smsCtrl", ['$scope', 'smsData', 'createDialog', '$http', '$filter', function($scope, smsData, createDialog, $http, $filter){...}
4
  • 1
    Can you post your code? I filtered about 150k of objects and took me 4-6 sec. BTW, you can implement loader Commented Nov 5, 2013 at 8:47
  • what do you mean "auto-filter", how do you filter your item Commented Nov 5, 2013 at 8:48
  • Also tell us how many data of each item you show? If there is many of data-binded fields for each array item then rendering of the list may cause performance issues. Commented Nov 5, 2013 at 9:02
  • @jason I mean that search starts immediately after I begin typing something in the input. Commented Nov 5, 2013 at 12:39

3 Answers 3

10

I've encountered something similar while helping a colleague (although the filtering of the search being manually triggered was desirable in our case) and came up with a similar but slightly more simple solution.

Use your original repeating div.

<div ng-repeat="user in users | filter:search">
    ...
</div>

Create an object for storing your user input.

$scope.search = {};
$scope.userInput = {};

Attach your input to this user input object.

<input type="text" ng-model="userInput.name" />
<input type="text" ng-model="userInput.phone" />
<input type="text" ng-model="userInput.city" />

Create a function which loops the properties of the user input object and copies them to your search object.

$scope.applySearch = function() {
    for(prop in $scope.userInput) {
        $scope.search[prop] = $scope.userInput[prop];
    }
};

Finally, create a button to call your search function.

<button ng-click="applySearch()">Search</search>

I hope this helps someone.

Sign up to request clarification or add additional context in comments.

2 Comments

This is really smart approach. Far better than accepted answer. Like it!
This is a good solution, but instead of looping through the properties, you could just do: Object.assign($scope.search, $scope.userInput);.
0

Maybe you can try add a debounce on it and forget the button.

Follow a link to a nice debounce code to apply any DOM created by Lars Gersmann. You can look his JSFiddle example of how its gonna work at the end of the article.

From pull request #2129 of AngularJS project at GitHub:

Also, a ng-update-model-debounce attribute will allow defering the actual model update after the last triggered event. This feature is not available in radio buttons.

I.e. ng-update-model-debounce="500" for 500ms

Follow bellow a nice approach of how to use debounce

/**
 * uiDebounce service provides a mechanism for creating a wrapper around a function 
 * that ensures that the wrapped function is not called more frequently than a
 * given time interval.
 *
 * @param {!Function} func The function to be wrapped (debounced)
 * @param {number} wait How long between called to func
 * @param {Boolean} immediate If true then the function is invoked on the first call to the
 * wrapper function, otherwise the call will not happen until after the wait time has expired
 * @return {Function} A debounced wrapper around the func function.
 *
 * @example
 * function lookup(id) { ... lookup something ... }
 * var debounceLookup = debounce(lookup, 2000, false);
 * $scope.doLookup = function(msg) {
 *   var promise = debounceLookup(msg);
 *   console.log('called lookup: ', promise);
 *   promise.then(function(value) {
 *     console.log('lookup returned:', value);
 *   });
 * };
 */
angular.module('ui.services').factory('uiDebounce', function($timeout, $q) {
  return function(func, wait, immediate) {
    var timeout;
    var deferred = $q.defer();
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if(!immediate) {
          deferred.resolve(func.apply(context, args));
          deferred = $q.defer();
        }
      };
      var callNow = immediate && !timeout;
      if ( timeout ) {
        $timeout.cancel(timeout);
      }
      timeout = $timeout(later, wait);
      if (callNow) {
        deferred.resolve(func.apply(context,args));
        deferred = $q.defer();
      }
      return deferred.promise;
    };
  };
});

Source: Github - Angular-UI

2 Comments

links may become dead after time. It's customary to copy at least some of the helpful text from the links in case this happens
@5uperdan tks for advice. Maybe now the answer is better to explain my approach for this problem.
0

I found the solution!

Change:

<div ng-repeat="user in users | filter:search" class="user_block" ng-include src="userTemplate"></div>

To:

<div ng-repeat="user in users" ng-hide="user.excludedByFilter" class="sms_user_block" ng-include src="userTemplate"></div>

Add "applySearchFilter" function to controller

    $scope.applySearchFilter = function() {
        var nameFilter = $scope.filters.name.toLowerCase();
        var phoneFilter = $scope.filters.phone;
        var cityFilter = $scope.filters.city;
        var showAll = 0 === nameFilter.length && 0 === phoneFilter.length && 0 === cityFilter.length;
        angular.forEach($scope.users, function(user) {
            if (showAll) {
                user.excludedByFilter = false;
            } else {
                user.excludedByFilter = (user.name.toLowerCase().indexOf(nameFilter) === -1) 
                                        || (user.phone.indexOf(phoneFilter) === -1) 
                                        || (user.city.indexOf(cityFilter) === -1);
            }
        });
    }

And add html code for filter button:

<a class="btn btn-primary" href="#" ng-click="applySearchFilter()">Apply filters</a>

And this works!

*Note, that I renamed ng-model="search.*" to ng-model="filters.*" in inputs.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.