如何在 AngularJS 上实现即时搜索的延迟?

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

约翰逊:

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 在内部就是这样做的,在阅读了这里的文档和其他主题后,我找不到一个具体的答案。

如果你能告诉我怎么推迟搜索的话,我会很感激的。

142213 次浏览

(参见下面的角度1.3解答答案。)

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

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

约翰逊:

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>

Angularjs 的退出/节流模型更新: http://jsfiddle.net/lgersman/vPsGb/3/

在您的例子中,除了像下面这样在 jsfiddle 代码中使用指令之外,没有其他事情可做:

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

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

你可以用它来节流/去弹跳 * 模型角度更新 * 角事件处理程序 ng-[事件] * jquery 事件处理程序

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

该指令将是橙色安培框架(https://github.com/lgersman/jquery.orangevolt-ampere)的一部分。

更新

现在它比以往任何时候都更容易(角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>

强制性活塞: Http://plnkr.co/dapmwf

我相信解决这个问题的最好方法是使用本阿尔曼的插件 JQuery 油门/退出。在我看来,没有必要延迟你形体中每一个能量场的事件。

只需将 $scope 包装起来。 $watch 处理函数在 $. 取消退出,如下所示:

$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

我用一个指令解决了这个问题,基本上就是把真正的 n- 模型绑定到我在指令中看到的一个特殊属性上,然后使用一个 deounce 服务来更新我的指令属性,这样用户就可以看到他绑定到 deounce-model 而不是 n- 模型的变量。

.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/

美元退票服务可以在这里找到: http://jsfiddle.net/Warspawn/6K7Kd/

受到最终绑定指令 http://jsfiddle.net/fctZH/12/的启发

角度1.3将有 ng- 模型选项去除,但在那之前,你必须使用计时器,如 Josue Ibarra 所说。然而,在他的代码中,他在每次按键时都会启动一个计时器。而且,他使用的是 setTimeout,在 Angular 中必须使用 $timeout 或在 setTimeout 的末尾使用 $application。

在角度1.3中,我会这样做:

HTML:

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

总监:

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

基本上,当 msg作用域变量发生变化时,您告诉 angle 运行 myDebouncedFunction()。属性 ng-model-options="{debounce: 1000}"确保 msg每秒只能更新一次。

为什么每个人都想使用手表? 你也可以使用一个功能:

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

对于那些在 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);
};
});

超文本标示语言

<input type="search" autocomplete="off" ng-model="keywords" ng-keyup="search()" placeholder="Search...">
 <input type="text"
ng-model ="criteria.searchtext""
ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
class="form-control"
placeholder="Search" >

现在我们可以设置 ng- 模型选项退出时间和模糊时,模型需要立即改变,否则在保存它将有旧的价值,如果延迟不完成。

仅供重定向到此处的用户使用:

正如在 Angular 1.3中介绍的,您可以使用 模型选择属性:

<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-model-options="{ debounce: 250 }"
/>

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