如何延迟AngularJS即时search?
我是AngularJS的新手,我有一个性能问题,我似乎无法解决。 我有即时search,但它有点滞后,因为它开始search每个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,我认为我需要做的是在search上延迟1秒,等待用户完成input,而不是在每个按键上执行操作。 AngularJS在内部完成这项工作,在阅读了这里的文档和其他主题之后,我找不到具体的答案。
如果我能延迟即时search,我将不胜感激。 谢谢。
(有关Angular 1.3解决scheme,请参阅下面的答案。)
这里的问题是,每次模型更改时都会执行search,这是input上的每个关键操作。
会有更干净的方法来做到这一点,但最简单的方法可能是切换绑定,以便您的控制器中定义一个$ scope属性,您的filter在其中运行。 这样你可以控制$ scopevariables的更新频率。 像这样的东西:
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>
UPDATE
现在比以前更容易了(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
旧方法:
这里是另外一个方法,除了angular本身,没有任何依赖。
您需要设置一个超时时间,并将您当前的string与以前的版本进行比较,如果两者相同,则执行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); });
这就进入了你的观点:
<input type="text" data-ng-model="searchStr"> <textarea> {{responseData}} </textarea>
强制性的抢注: http ://plnkr.co/dAPmwf
在Angular 1.3中,我会这样做:
HTML:
<input ng-model="msg" ng-model-options="{debounce: 1000}">
控制器:
$scope.$watch('variableName', function(nVal, oVal) { if (nVal !== oVal) { myDebouncedFunction(); } });
基本上,当msg scopevariables发生变化时,您正在告诉angular运行myDebouncedFunction()。 ng-model-options =“{debounce:1000}”属性确保msg只能每秒更新一次。
<input type="text" ng-model ="criteria.searchtext"" ng-model-options="{debounce: {'default': 1000, 'blur': 0}}" class="form-control" placeholder="Search" >
现在我们可以设置ng-model-options随着时间的推移而去抖动,当模糊时,模型需要立即改变,否则保存在延迟没有完成的时候会有较大的值。
去抖/扼杀模型更新为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”的单个angular度指令组成的一小段代码,利用http://benalman.com/projects/jquery-throttle-debounce-plugin/可以连接到任何dom元素。; 该指令重新sorting附加的事件处理程序,以便它可以控制何时调节事件。
你可以使用它来限制/ debouncing *模型angular度更新*angular事件处理程序ng- [事件] * jquery事件处理程序
看一下: http : //jsfiddle.net/lgersman/vPsGb/3/
该指令将成为Orangevolt Ampere框架的一部分( https://github.com/lgersman/jquery.orangevolt-ampere )。
对于那些在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...">
我相信解决这个问题的最好方法是使用Ben Alman的插件jQuery油门/去抖动 。 在我看来,没有必要延迟你的表单中的每一个领域的事件。
只需包装你的$范围$ watch手柄函数$ .debounce就像这样:
$scope.$watch("searchText", $.debounce(1000, function() { console.log($scope.searchText); }), true);
仅针对这里redirect的用户:
正如在Angular 1.3
介绍的,你可以使用ng-model-options属性:
<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" ng-model-options="{ debounce: 250 }" />
另一个解决scheme是添加一个延迟function模型更新。 简单的指令似乎有一个窍门:
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-model
代替ng-model
并定义所需的data-delay
。
演示: http : //plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview
我用一个指令解决了这个问题,这个指令基本上是把实际的ng-model绑定到我在指令中看到的一个特殊的属性上,然后使用一个debounce服务更新我的指令属性,这样用户就可以监视variables他绑定到debounce模型而不是ng模型。
.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服务可以在这里find: http : //jsfiddle.net/Warspawn/6K7Kd/
受到最终绑定指令的启发 http://jsfiddle.net/fctZH/12/
Angular 1.3会有ng-model-options的debounce,但是在那之前,你必须使用像Josue Ibarra这样的计时器。 然而,在他的密码中,他在每个按键上都启动了一个计时器。 此外,他正在使用setTimeout,在Angular中必须使用$ timeout或在setTimeout结束时使用$ apply。
为什么每个人都想用手表? 你也可以使用一个函数:
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); };
我认为这里最简单的方法是预加载JSON,或者在$dirty
上加载一次,然后filtersearch将完成其余部分。 这将为您节省额外的http调用,并且使用预加载的数据更快。 记忆会受伤,但它的价值。