Cómo retrasar la búsqueda instantánea AngularJS?


Soy nuevo en AngularJS, y tengo un problema de rendimiento que no puedo abordar. Tengo búsqueda instantánea, pero es algo rezagado, ya que comienza a buscar en cada 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>

Los datos JSON no son tan grandes, solo 300KB, creo que lo que necesito 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 en cada pulsación de tecla. AngularJS hace esto internamente, y después leyendo documentos y otros temas aquí no pude encontrar una respuesta específica.

Agradecería cualquier consejo sobre cómo puedo retrasar la búsqueda instantánea. Gracias.

Author: braincomb, 0000-00-00

12 answers

(Ver respuesta abajo para una solución Angular 1.3.)

El problema aquí es que la búsqueda se ejecutará cada vez que el modelo cambie, que es cada acción keyup en una entrada.

Habría formas más limpias de hacer esto, pero probablemente la forma más fácil sería cambiar el enlace para que tenga una propiedad defined scope definida dentro de su Controlador en el que opera su filtro. De esta manera puede controlar la frecuencia con la que se actualiza esa variable scope 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>
 120
Author: Jason Aden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-25 07:29:04

UPDATE

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

<input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">

Émbolo actualizado:
http://plnkr.co/edit/4V13gK

Documentación sobre modelos de NG:
https://docs.angularjs.org/api/ng/directive/ngModelOptions

Método antiguo:

Aquí hay otro método sin dependencias más allá de angular.

Necesita establecer un tiempo de espera y comparar su actual cadena 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 émbolo obligatorio: http://plnkr.co/dAPmwf

 294
Author: Josue Alexander Ibarra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-26 01:21:13

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ás diciendo a angular que ejecute myDebouncedFunction(), cuando la variable msg scope cambie. El atributo ng-model-options= " {debounce: 1000}" asegura que msg solo pueda actualizarse una vez por segundo.

 33
Author: Michael Falck Wedelgård,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-27 11:45:24
 <input type="text"
    ng-model ="criteria.searchtext""  
    ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
    class="form-control" 
    placeholder="Search" >

Ahora podemos establecer ng-model-options debounce con el tiempo y cuando el desenfoque, el modelo debe cambiarse inmediatamente, de lo contrario al guardar tendrá un valor más antiguo si el retraso no se completa.

 10
Author: Ali Adravi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-14 14:50:32

Rebotada / estrangulado actualizaciones de modelo 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 de esta manera:

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

Es básicamente una pequeña pieza de código que consiste en una sola directiva angular llamada "ng-ampere-debounce" utilizando http://benalman.com/projects/jquery-throttle-debounce-plugin / que se puede adjuntar a cualquier elemento dom. La directiva reordena el anexo controladores de eventos para que pueda controlar cuándo acelerar los eventos.

Se puede utilizar para la limitación/debouncing * actualizaciones angulares del modelo * controlador de eventos angular ng-[evento] * controladores de eventos de jquery

Echa un vistazo: http://jsfiddle.net/lgersman/vPsGb/3 /

La directiva formará parte del marco Orangevolt Ampere ( https://github.com/lgersman/jquery.orangevolt-ampere).

 6
Author: lgersman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-15 13:14:43

Para aquellos que usan 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...">
 6
Author: Vinoth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-16 07:20:02

Solo para los usuarios redirigidos aquí:

Como se introdujo en Angular 1.3 puede usar ng-model-options atributo:

<input 
       id="searchText" 
       type="search" 
       placeholder="live search..." 
       ng-model="searchText"
       ng-model-options="{ debounce: 250 }"
/>
 6
Author: Morteza Tourani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-22 05:08:07

Creo que la mejor manera de resolver este problema es usando el plugin de Ben Alman jQuery throttle / debounce. En mi opinión, no hay necesidad de retrasar los eventos de cada campo en el formulario.

Simplemente envuelve tu scope scope.function función de manejo de reloj en $.debounce así:

$scope.$watch("searchText", $.debounce(1000, function() {
    console.log($scope.searchText);
}), true);
 5
Author: Daniel Popov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-28 20:16:24

Otra solución es agregar una funcionalidad de retardo a la actualización del modelo. La simple directiva 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 simplemente usa delayed-model en lugar de ng-model y define el data-delay deseado.

Demo: http://plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview

 3
Author: dfsq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-07 09:02:26

Resolví este problema con una directiva que básicamente lo que hace es enlazar el modelo ng real en un atributo especial que observo en la directiva, luego utilizando un servicio de debounce actualizo mi atributo de directiva, para que el usuario observe la variable que enlaza a debounce-model 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 }

Demo en jsfiddle: http://jsfiddle.net/6K7Kd/37/

El servicio deb debounce se puede encontrar aquí: http://jsfiddle.net/Warspawn/6K7Kd/

Inspirado en eventuallyBind directiva http://jsfiddle.net/fctZH/12 /

 0
Author: Ofir D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-19 22:53:08

Angular 1.3 tendrá opciones ng-model-debounce, pero hasta entonces, tienes que usar un temporizador como dijo Josué Ibarra. Sin embargo, en su código lanza un temporizador en cada pulsación de tecla. Además, está usando setTimeout, cuando en Angular uno tiene que usar tim timeout o use apply apply al final de setTimeout.

 0
Author: F.A.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-25 15:45:10
 0
Author: ,
Warning: date() expects parameter 2 to be long, string given in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61