Jak opóźnić AngularJS instant search?

Jestem nowy w AngularJS i mam problem z wydajnością, którego nie mogę rozwiązać. Mam natychmiastowe wyszukiwanie, ale jest nieco opóźnione, ponieważ zaczyna szukać na każdym 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>

Dane JSON nie są nawet tak duże, tylko 300KB, myślę, że to, co muszę osiągnąć, to umieścić opóźnienie ~1 SEK na wyszukiwaniu, aby czekać na użytkownika, aby zakończyć pisanie, zamiast wykonywania akcji na każdym naciśnięciu klawisza. AngularJS robi to wewnętrznie, a po czytając dokumenty i inne tematy nie mogłem znaleźć konkretnej odpowiedzi.

Będę wdzięczny za wszelkie wskazówki, jak Mogę opóźnić natychmiastowe wyszukiwanie. Dzięki.
Author: braincomb, 0000-00-00

12 answers

(Zobacz odpowiedź poniżej dla rozwiązania kątowego 1.3.)

Problem polega na tym, że wyszukiwanie będzie wykonywane za każdym razem, gdy zmieni się model, czyli każda akcja keyup na wejściu.

Byłyby czystsze sposoby na to, ale prawdopodobnie najprostszym sposobem byłoby przełączenie wiązania tak, aby mieć właściwość $ scope zdefiniowaną wewnątrz kontrolera, na którym działa filtr. W ten sposób możesz kontrolować częstotliwość aktualizacji zmiennej $scope. Coś jak to:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-25 07:29:04

UPDATE

Teraz jest to łatwiejsze niż kiedykolwiek (Angular 1.3), wystarczy dodać opcję debounce w modelu.

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

Updated plunker:
http://plnkr.co/edit/4V13gK

Dokumentacja dotycząca ngModelOptions:
https://docs.angularjs.org/api/ng/directive/ngModelOptions

Stara metoda:

Oto kolejna metoda bez zależności poza samym kanałem.

Musisz ustawić timeout i porównać swój bieżący string z poprzednią wersją, jeśli obie są takie same, to wykonuje wyszukiwanie.

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

I to idzie do twojej opinii:

<input type="text" data-ng-model="searchStr">

<textarea> {{responseData}} </textarea>

Obowiązkowy plunker: 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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-26 01:21:13

W Angular 1.3 zrobiłbym tak:

HTML:

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

Kontroler:

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

W zasadzie każesz angular uruchomić mydebouncedfunction (), gdy zmienna zakresu MSG się zmieni. Atrybut ng-model-options= "{debounce: 1000} " sprawia, że msg może aktualizować tylko raz na sekundę.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
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" >

Teraz możemy ustawić ng-model-options debounce z czasem i kiedy blur, model musi zostać natychmiast zmieniony, w przeciwnym razie przy zapisie będzie miał starszą wartość, jeśli opóźnienie nie zostanie zakończone.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-14 14:50:32

Debuted / throttled model updates for angularjs : http://jsfiddle.net/lgersman/vPsGb/3/

W Twoim przypadku nie ma nic więcej do zrobienia niż użycie dyrektywy w kodzie jsfiddle w następujący sposób:

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

To w zasadzie mały fragment kodu składający się z pojedynczej dyrektywy kątowej o nazwie "ng-ampere-debounce" wykorzystującej http://benalman.com/projects/jquery-throttle-debounce-plugin / , który może być dołączony do dowolnego elementu dom. Dyrektywa zmienia kolejność załączonych obsługa zdarzeń, dzięki czemu może kontrolować, kiedy należy Dławić zdarzenia.

Możesz go użyć do dławienia/debouncingu * aktualizacje kątowe modelu * angular event handler ng-[event] * obsługa zdarzeń jquery

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

Dyrektywa będzie częścią ram 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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-15 13:14:43

Dla tych, którzy używają keyup/keydown w znacznikach HTML. To nie używa zegarka.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-16 07:20:02

Tylko dla użytkowników przekierowanych tutaj:

Jak wprowadzono w Angular 1.3 możesz użyć ng-model-options atrybut:

<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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-22 05:08:07

Uważam, że najlepszym sposobem rozwiązania tego problemu jest użycie wtyczki Ben Alman jQuery throttle / debounce. Moim zdaniem nie ma potrzeby opóźniania zdarzeń z każdego pola w formularzu.

Po prostu zapakuj swój $ scope.$funkcja obsługi zegarka w $.debounce tak:

$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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 20:16:24

Innym rozwiązaniem jest dodanie funkcji opóźnienia do aktualizacji modelu. Prosta dyrektywa wydaje się robić sztuczkę:

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

Użycie:

<input delayed-model="searchText" data-delay="500" id="searchText" type="search" placeholder="live search..." />

Więc wystarczy użyć delayed-model zamiast ng-model i zdefiniować pożądane data-delay.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-07 09:02:26

Rozwiązałem ten problem za pomocą dyrektywy, która zasadniczo polega na powiązaniu prawdziwego modelu ng ze specjalnym atrybutem, który Oglądam w dyrektywie, a następnie za pomocą usługi debounce aktualizuję atrybut dyrektywy, więc użytkownik obserwuje zmienną, którą wiąże z debounce-model zamiast 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);
  }
};
});

Użycie:

<input type="text" debounce-delay="1000" debounce-model="search"></input>

I w kontrolerze:

    $scope.search = "";
    $scope.$watch('search', function (newVal, oldVal) {
      if(newVal === oldVal){
        return;
      }else{ //do something meaningful }

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

Usługa $debounce można znaleźć tutaj: http://jsfiddle.net/Warspawn/6K7Kd/

Zainspirowany ostatecznie dyrektywą 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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-19 22:53:08

Angular 1.3 będzie miał debounce ng-model-options, ale do tego czasu musisz użyć timera, jak powiedział Josue Ibarra. Jednak w swoim kodzie uruchamia timer przy każdym naciśnięciu klawisza. Ponadto używa setTimeout, gdy w Angular trzeba użyć $timeout lub $apply na końcu 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/doraprojects.net/template/agent.layouts/content.php on line 54
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/doraprojects.net/template/agent.layouts/content.php on line 54