How to put a delay on AngularJS instant search?

前端 未结 13 1457
醉梦人生
醉梦人生 2020-11-30 16:55

I have a performance issue that I can\'t seem to address. I have an instant search but it\'s somewhat laggy, since it starts searching on each keyup().

相关标签:
13条回答
  • 2020-11-30 17:27

    UPDATE

    Now it's easier than ever (Angular 1.3), just add a debounce option on the model.

    <input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">

    Updated plunker:
    http://plnkr.co/edit/4V13gK

    Documentation on ngModelOptions:
    https://docs.angularjs.org/api/ng/directive/ngModelOptions

    Old method:

    Here's another method with no dependencies beyond angular itself.

    You need set a timeout and compare your current string with the past version, if both are the same then it performs the search.

    $scope.$watch('searchStr', function (tmpStr)
    {
      if (!tmpStr || tmpStr.length == 0)
        return 0;
       $timeout(function() {
    
        // if searchStr is still the same..
        // go ahead and retrieve the data
        if (tmpStr === $scope.searchStr)
        {
          $http.get('//echo.jsontest.com/res/'+ tmpStr).success(function(data) {
            // update the textarea
            $scope.responseData = data.res; 
          });
        }
      }, 1000);
    });
    

    and this goes into your view:

    <input type="text" data-ng-model="searchStr">
    
    <textarea> {{responseData}} </textarea>
    

    The mandatory plunker: http://plnkr.co/dAPmwf

    0 讨论(0)
  • 2020-11-30 17:28

    In Angular 1.3 I would do this:

    HTML:

    <input ng-model="msg" ng-model-options="{debounce: 1000}">
    

    Controller:

    $scope.$watch('variableName', function(nVal, oVal) {
        if (nVal !== oVal) {
            myDebouncedFunction();
        }
    });
    

    Basically you're telling angular to run myDebouncedFunction(), when the the msg scope variable changes. The attribute ng-model-options="{debounce: 1000}" makes sure that msg can only update once a second.

    0 讨论(0)
  • 2020-11-30 17:29

    (See answer below for a Angular 1.3 solution.)

    The issue here is that the search will execute every time the model changes, which is every keyup action on an input.

    There would be cleaner ways to do this, but probably the easiest way would be to switch the binding so that you have a $scope property defined inside your Controller on which your filter operates. That way you can control how frequently that $scope variable is updated. Something like this:

    JS:

    var App = angular.module('App', []);
    
    App.controller('DisplayController', function($scope, $http, $timeout) {
        $http.get('data.json').then(function(result){
            $scope.entries = result.data;
        });
    
        // This is what you will bind the filter to
        $scope.filterText = '';
    
        // Instantiate these variables outside the watch
        var tempFilterText = '',
            filterTextTimeout;
        $scope.$watch('searchText', function (val) {
            if (filterTextTimeout) $timeout.cancel(filterTextTimeout);
    
            tempFilterText = val;
            filterTextTimeout = $timeout(function() {
                $scope.filterText = tempFilterText;
            }, 250); // delay 250 ms
        })
    });
    

    HTML:

    <input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
    <div class="entry" ng-repeat="entry in entries | filter:filterText">
        <span>{{entry.content}}</span>
    </div>
    
    0 讨论(0)
  • 2020-11-30 17:29

    Another solution is to add a delay functionality to model update. The simple directive seems to do a trick:

    app.directive('delayedModel', function() {
        return {
            scope: {
                model: '=delayedModel'
            },
            link: function(scope, element, attrs) {
    
                element.val(scope.model);
    
                scope.$watch('model', function(newVal, oldVal) {
                    if (newVal !== oldVal) {
                        element.val(scope.model);        
                    }
                });
    
                var timeout;
                element.on('keyup paste search', function() {
                    clearTimeout(timeout);
                    timeout = setTimeout(function() {
                        scope.model = element[0].value;
                        element.val(scope.model);
                        scope.$apply();
                    }, attrs.delay || 500);
                });
            }
        };
    });
    

    Usage:

    <input delayed-model="searchText" data-delay="500" id="searchText" type="search" placeholder="live search..." />
    

    So you just use delayed-model in place of ng-model and define desired data-delay.

    Demo: http://plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview

    0 讨论(0)
  • 2020-11-30 17:30
     <input type="text"
        ng-model ="criteria.searchtext""  
        ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
        class="form-control" 
        placeholder="Search" >
    

    Now we can set ng-model-options debounce with time and when blur, model need to be changed immediately otherwise on save it will have older value if delay is not completed.

    0 讨论(0)
  • 2020-11-30 17:32

    I believe that the best way to solve this problem is by using Ben Alman's plugin jQuery throttle / debounce. In my opinion there is no need to delay the events of every single field in your form.

    Just wrap your $scope.$watch handling function in $.debounce like this:

    $scope.$watch("searchText", $.debounce(1000, function() {
        console.log($scope.searchText);
    }), true);
    
    0 讨论(0)
提交回复
热议问题