search - ¿Cómo poner un retraso en la búsqueda instantánea de AngularJS?
filter timeout (13)
Solo para usuarios redirigidos aquí:
Como se presentó en Angular 1.3
, puede usar el atributo https://docs.angularjs.org/api/ng/directive/ngModelOptions :
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-model-options="{ debounce: 250 }"
/>
Soy nuevo en AngularJS, y tengo un problema de rendimiento que parece que no puedo abordar. Tengo búsqueda instantánea pero es un poco lenta, ya que comienza a buscar en cada tecla ().
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>
Los datos de JSON no son tan grandes, solo 300 KB, creo que lo que debo lograr es poner un retraso de ~ 1 segundo en la búsqueda para esperar a que el usuario termine de escribir, en lugar de realizar la acción con cada pulsación de tecla. AngularJS hace esto internamente, y después de leer documentos y otros temas aquí no pude encontrar una respuesta específica.
Agradecería cualquier sugerencia sobre cómo puedo retrasar la búsqueda instantánea. Gracias.
¿Por qué todos quieren usar reloj? También puedes usar una función:
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);
};
(Consulte la respuesta a continuación para una solución Angular 1.3.)
El problema aquí es que la búsqueda se ejecutará cada vez que cambie el modelo, que es cada acción de tecla en una entrada.
Habría maneras más limpias de hacerlo, pero probablemente la forma más fácil sería cambiar el enlace para que tenga una propiedad $ scope definida dentro de su Controlador en el que opera su filtro. De esta forma, puede controlar la frecuencia con la que se actualiza la variable $ scope. Algo como esto:
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>
Actualizaciones de modelo desbocadas / reguladas para angularjs: http://jsfiddle.net/lgersman/vPsGb/3/
En su caso, no hay nada más que hacer que usar la directiva en el código jsfiddle como este:
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-ampere-debounce
/>
Básicamente es una pequeña porción de código que consiste en una única directiva angular denominada "ng-ampere-rebote" que utiliza http://benalman.com/projects/jquery-throttle-debounce-plugin/ que se puede adjuntar a cualquier elemento dom. La directiva reordena los manejadores de eventos adjuntos para que pueda controlar cuándo estrangular los eventos.
Puede usarlo para limitar / eliminar rebotes * actualizaciones angulares modelo * controlador de eventos angulares ng- [evento] * controladores de eventos jquery
Eche un vistazo: http://jsfiddle.net/lgersman/vPsGb/3/
La directiva formará parte del marco Orangevolt Ampere ( https://github.com/lgersman/jquery.orangevolt-ampere ).
Angular 1.3 tendrá rechazo de opciones de modelo ng, pero hasta entonces, tienes que usar un temporizador como dijo Josue Ibarra. Sin embargo, en su código lanza un temporizador cada vez que presiona una tecla. Además, él está usando setTimeout, cuando en Angular uno tiene que usar $ timeout o usar $ apply al final de setTimeout.
Creo que la manera más fácil aquí es precargar el json o cargarlo una vez en $dirty
y luego la búsqueda del filtro se encargará del resto. Esto te ahorrará las llamadas http adicionales y es mucho más rápido con los datos precargados. La memoria duele, pero vale la pena.
Creo que la mejor manera de resolver este problema es usar el complemento http://benalman.com/projects/jquery-throttle-debounce-plugin/ Ben Alman. En mi opinión, no hay necesidad de retrasar los eventos de cada campo en su formulario.
Simplemente ajuste su $ scope. $ Watch función de manejo en $ .debounce como este:
$scope.$watch("searchText", $.debounce(1000, function() {
console.log($scope.searchText);
}), true);
En Angular 1.3, haría esto:
HTML:
<input ng-model="msg" ng-model-options="{debounce: 1000}">
Controlador:
$scope.$watch(''variableName'', function(nVal, oVal) {
if (nVal !== oVal) {
myDebouncedFunction();
}
});
Básicamente, le está diciendo a angular que ejecute myDebouncedFunction (), cuando cambia la variable de ámbito msg. El atributo ng-model-options = "{rebote: 1000}" asegura que msg solo puede actualizar una vez por segundo.
Otra solución es agregar una funcionalidad de demora a la actualización del modelo. La directiva simple parece hacer un truco:
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);
});
}
};
});
Uso:
<input delayed-model="searchText" data-delay="500" id="searchText" type="search" placeholder="live search..." />
Así que solo usa delayed-model
en lugar de ng-model
y define el data-delay
deseado.
Demostración: http://plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview
Para aquellos que utilizan keyup / keydown en el marcado HTML. Esto no usa reloj.
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...">
Resolví este problema con una directiva que, básicamente, lo que hace es vincular el modelo ng real en un atributo especial que miro en la directiva, luego, usando un servicio antirrebote, actualizo mi atributo directivo, para que el usuario vea la variable que se une al modelo de rebote en lugar de 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);
}
};
});
Uso:
<input type="text" debounce-delay="1000" debounce-model="search"></input>
Y en el controlador:
$scope.search = "";
$scope.$watch(''search'', function (newVal, oldVal) {
if(newVal === oldVal){
return;
}else{ //do something meaningful }
Demostración en jsfiddle: http://jsfiddle.net/6K7Kd/37/
el servicio $ rebote se puede encontrar aquí: http://jsfiddle.net/Warspawn/6K7Kd/
Inspirado por la directiva http://jsfiddle.net/fctZH/12/
ACTUALIZAR
Ahora es más fácil que nunca (Angular 1.3), simplemente agregue una opción antirrebote en el modelo.
<input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">
Plunker actualizado:
http://plnkr.co/edit/4V13gK
Documentación sobre ngModelOptions:
https://docs.angularjs.org/api/ng/directive/ngModelOptions
Método antiguo:
Aquí hay otro método sin dependencias más allá del propio angular.
Necesita establecer un tiempo de espera y comparar su cadena actual con la versión anterior, si ambas son iguales, entonces realiza la búsqueda.
$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);
});
y esto entra en tu punto de vista:
<input type="text" data-ng-model="searchStr">
<textarea> {{responseData}} </textarea>
El plunker obligatorio: http://plnkr.co/dAPmwf
<input type="text"
ng-model ="criteria.searchtext""
ng-model-options="{debounce: {''default'': 1000, ''blur'': 0}}"
class="form-control"
placeholder="Search" >
Ahora podemos establecer que ng-model-options rebote con el tiempo y cuando el desenfoque, el modelo deba cambiarse de inmediato, de lo contrario, al guardar, tendrá un valor más antiguo si el retraso no se completa.