我有一个似乎无法解决的性能问题。我有一个即时搜索,但它有点滞后,因为它开始搜索每个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在内部执行此操作,在阅读了这里的文档和其他主题后,我找不到具体的答案。
我将不胜感激任何关于如何延迟即时搜索的指示。
更新
现在它比以往任何时候都容易(Angular 1.3(,只需在模型上添加一个去抖动选项。
<input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">
更新的弹道:
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
(有关 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>
在 Angular 1.3 中,我会这样做:
.HTML:
<input ng-model="msg" ng-model-options="{debounce: 1000}">
控制器:
$scope.$watch('variableName', function(nVal, oVal) {
if (nVal !== oVal) {
myDebouncedFunction();
}
});
基本上你告诉 angular 运行myDebouncedFunction()
,当msg
范围变量改变时。属性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 随时间去抖动,当模糊时,模型需要立即更改,否则在保存时如果未完成延迟,它将具有较旧的值。
对于那些在 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...">
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-[事件]* jquery事件处理程序
看一看 : http://jsfiddle.net/lgersman/vPsGb/3/
该指令将成为橙伏安框架(https://github.com/lgersman/jquery.orangevolt-ampere(的一部分。
仅适用于重定向到此处的用户:
如Angular 1.3
中所述,您可以使用 ng-model-options 属性:
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-model-options="{ debounce: 250 }"
/>
我相信解决这个问题的最好方法是使用 Ben Alman 的插件 jQuery 节流/去抖动。在我看来,没有必要延迟表单中每个字段的事件。
只需将您的 $scope.$watch 处理函数包装在 $.debounce 中,如下所示:
$scope.$watch("searchText", $.debounce(1000, function() {
console.log($scope.searchText);
}), true);
另一种解决方案是向模型更新添加延迟功能。简单的指令似乎做了一个技巧:
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 绑定到我在指令中观察的特殊属性上,然后使用去抖服务我更新我的指令属性,因此用户监视他绑定到去抖动模型而不是 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服务可以在这里找到:http://jsfiddle.net/Warspawn/6K7Kd/
灵感来自最终绑定指令 http://jsfiddle.net/fctZH/12/
Angular 1.3 将具有 ng-model-options 去抖动功能,但在此之前,您必须像 Josue Ibarra 所说的那样使用计时器。但是,在他的代码中,他在每次按键时都会启动一个计时器。此外,他正在使用setTimeout,而在Angular中,人们必须在setTimeout结束时使用$timeout或使用$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
加载一次,然后过滤器搜索将处理其余的工作。这将为您节省额外的 http 调用,并且预加载数据的速度要快得多。记忆会很痛,但值得。