148

我有一个似乎无法解决的性能问题。我有一个即时搜索,但它有点滞后,因为它开始搜索每个keyup().

JS:

var App = angular.module('App', []);

App.controller('DisplayController', function($scope, $http) {
$http.get('data.json').then(function(result){
    $scope.entries = result.data;
});
});

HTML:

<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:searchText">
<span>{{entry.content}}</span>
</div>

JSON数据甚至没有那么大,只有300KB,我认为我需要完成的是在搜索上延迟约1秒以等待用户完成输入,而不是在每次击键时执行操作。AngularJS 在内部执行此操作,在阅读了此处的文档和其他主题后,我找不到具体的答案。

我将不胜感激有关如何延迟即时搜索的任何指示。

4

13 回答 13

301

更新

现在比以往任何时候都容易(Angular 1.3),只需在模型上添加一个去抖动选项。

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

更新的 plunker:
http ://plnkr.co/edit/4V13gK

关于 ngModelOptions 的文档:
https ://docs.angularjs.org/api/ng/directive/ngModelOptions

老方法:

这是另一种不依赖于角度本身的方法。

您需要设置超时并将当前字符串与过去的版本进行比较,如果两者相同,则执行搜索。

$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);
});

这进入你的视野:

<input type="text" data-ng-model="searchStr">

<textarea> {{responseData}} </textarea>

强制性 plunker: http ://plnkr.co/dAPmwf

于 2013-08-28T17:34:14.630 回答
122

(有关 Angular 1.3 解决方案,请参阅下面的答案。)

这里的问题是每次模型更改时都会执行搜索,这是对输入的每个键操作。

会有更简洁的方法来做到这一点,但可能最简单的方法是切换绑定,以便您在控制器中定义一个 $scope 属性,您的过滤器在该属性上运行。这样您就可以控制 $scope 变量的更新频率。像这样的东西:

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>
于 2013-03-08T23:18:49.727 回答
34

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.

于 2015-03-20T20:15:59.610 回答
10
 <input type="text"
    ng-model ="criteria.searchtext""  
    ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
    class="form-control" 
    placeholder="Search" >

现在我们可以设置 ng-model-options 随时间去抖动,当模糊时,需要立即更改模型,否则在保存时如果延迟未完成,它将具有较旧的值。

于 2015-08-14T14:50:32.997 回答
9

对于那些在 HTML 标记中使用 keyup/keydown 的人。这不使用手表。

JS

app.controller('SearchCtrl', function ($scope, $http, $timeout) {
  var promise = '';
  $scope.search = function() {
    if(promise){
      $timeout.cancel(promise);
    }
    promise = $timeout(function() {
    //ajax call goes here..
    },2000);
  };
});

HTML

<input type="search" autocomplete="off" ng-model="keywords" ng-keyup="search()" placeholder="Search...">
于 2015-05-16T07:20:02.367 回答
6

angularjs 的去抖动/节流模型更新:http: //jsfiddle.net/lgersman/vPsGb/3/

在您的情况下,除了在 jsfiddle 代码中使用指令之外,别无他法,如下所示:

<input 
    id="searchText" 
    type="search" 
    placeholder="live search..." 
    ng-model="searchText" 
    ng-ampere-debounce
/>

它基本上是一小段代码,由一个名为“ng-ampere-debounce”的单个角度指令组成,该指令利用http://benalman.com/projects/jquery-throttle-debounce-plugin/可以附加到任何 dom 元素。该指令重新排序附加的事件处理程序,以便它可以控制何时限制事件。

您可以将它用于节流/去抖动 * 模型角度更新 * 角度事件处理程序 ng-[event] * jquery 事件处理程序

看看:http: //jsfiddle.net/lgersman/vPsGb/3/

该指令将成为 Orangevolt Ampere 框架 ( https://github.com/lgersman/jquery.orangevolt-ampere ) 的一部分。

于 2013-08-15T13:14:43.613 回答
6

仅适用于此处重定向的用户:

如中所述,Angular 1.3您可以使用ng-model-options属性:

<input 
       id="searchText" 
       type="search" 
       placeholder="live search..." 
       ng-model="searchText"
       ng-model-options="{ debounce: 250 }"
/>
于 2016-05-22T05:08:07.413 回答
5

我相信解决这个问题的最好方法是使用 Ben Alman 的插件jQuery throttle / debounce。在我看来,没有必要延迟表单中每个字段的事件。

只需将您的 $scope.$watch 处理函数包装在 $.debounce 中,如下所示:

$scope.$watch("searchText", $.debounce(1000, function() {
    console.log($scope.searchText);
}), true);
于 2013-10-18T13:19:51.877 回答
3

另一种解决方案是为模型更新添加延迟功能。简单的指令似乎起到了作用:

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);
            });
        }
    };
});

用法:

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

因此,您只需使用delayed-modelng-model定义所需的data-delay.

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

于 2014-01-13T12:01:17.113 回答
0

Angular 1.3 将有 ng-model-options debounce,但在那之前,你必须使用 Josue Ibarra 所说的计时器。然而,在他的代码中,他在每次按键时都会启动一个计时器。此外,他正在使用 setTimeout,而在 Angular 中,必须使用 $timeout 或在 setTimeout 结束时使用 $apply。

于 2014-09-25T15:45:10.410 回答
0

我用一个指令解决了这个问题,基本上它的作用是将真正的 ng-model 绑定到我在指令中观察的特殊属性上,然后使用去抖动服务更新我的指令属性,因此用户观察变量他绑定到 debounce-model 而不是 ng-model。

.directive('debounceDelay', function ($compile, $debounce) {
return {
  replace: false,
  scope: {
    debounceModel: '='
  },
  link: function (scope, element, attr) {
    var delay= attr.debounceDelay;
    var applyFunc = function () {
      scope.debounceModel = scope.model;
    }
    scope.model = scope.debounceModel;
    scope.$watch('model', function(){
      $debounce(applyFunc, delay);
    });
    attr.$set('ngModel', 'model');
    element.removeAttr('debounce-delay'); // so the next $compile won't run it again!

   $compile(element)(scope);
  }
};
});

用法:

<input type="text" debounce-delay="1000" debounce-model="search"></input>

在控制器中:

    $scope.search = "";
    $scope.$watch('search', function (newVal, oldVal) {
      if(newVal === oldVal){
        return;
      }else{ //do something meaningful }

jsfiddle 中的演示:http: //jsfiddle.net/6K7Kd/37/

$debounce 服务可以在这里找到:http: //jsfiddle.net/Warspawn/6K7Kd/

灵感来自 finallyBind 指令 http://jsfiddle.net/fctZH/12/

于 2014-03-19T20:47:44.270 回答
0

为什么大家都想用手表?您还可以使用一个函数:

var tempArticleSearchTerm;
$scope.lookupArticle = function (val) {
    tempArticleSearchTerm = val;

    $timeout(function () {
        if (val == tempArticleSearchTerm) {
            //function you want to execute after 250ms, if the value as changed

        }
    }, 250);
}; 
于 2015-04-03T07:04:05.587 回答
0

我认为这里最简单的方法是预加载 json 或加载一次$dirty,然后过滤器搜索将处理其余部分。这将为您节省额外的 http 调用,并且使用预加载的数据更快。记忆会受伤,但这是值得的。

于 2017-03-15T09:13:36.067 回答