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()
.
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
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.
(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>
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
.
<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.
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);