¿Cómo poner un retraso en la búsqueda instantánea de AngularJS?

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:

  
{{entry.content}}

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.

(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:

  
{{entry.content}}

ACTUALIZAR

Ahora es más fácil que nunca (Angular 1.3), simplemente agregue una opción antirrebote en el modelo.

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:

   

El plunker obligatorio: http://plnkr.co/dAPmwf

En Angular 1.3, haría esto:

HTML:

  

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.

   

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.

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:

  

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

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

  

Solo para usuarios redirigidos aquí:

Como se presentó en Angular 1.3 , puede usar el atributo ng-model-options :

  

Creo que la mejor manera de resolver este problema es usar el complemento jQuery throttle / rebote de 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); 

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:

  

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

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:

  

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 eventBind http://jsfiddle.net/fctZH/12/

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.

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

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