最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Applying a angularjs filter after "Apply" button click - Stack Overflow

programmeradmin3浏览0评论

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){...}

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){...}
Share Improve this question edited Nov 5, 2013 at 12:22 sashok1337 asked Nov 5, 2013 at 8:43 sashok1337sashok1337 1,0191 gold badge7 silver badges14 bronze badges 4
  • 1 Can you post your code? I filtered about 150k of objects and took me 4-6 sec. BTW, you can implement loader – Maxim Shoustin Commented Nov 5, 2013 at 8:47
  • what do you mean "auto-filter", how do you filter your item – jason 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. – ardentum-c Commented Nov 5, 2013 at 9:02
  • @jason I mean that search starts immediately after I begin typing something in the input. – sashok1337 Commented Nov 5, 2013 at 12:39
Add a ment  | 

3 Answers 3

Reset to default 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.

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

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.

发布评论

评论列表(0)

  1. 暂无评论