it-swarm.com.de

Wie kann ich die AngularJS-Schnellsuche verzögern?

Ich bin neu bei AngularJS und habe ein Leistungsproblem, das ich nicht ansprechen kann. Ich habe eine sofortige Suche, aber sie ist etwas verzögert, da bei jedem keyup () gesucht wird.

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>

Die JSON-Daten sind nicht einmal so groß, nur 300 KB. Ich denke, ich muss die Suche mit einer Verzögerung von ~ 1 Sek. Einstellen, um zu warten, bis der Benutzer mit der Eingabe fertig ist, anstatt die Aktion bei jedem Tastenanschlag auszuführen. AngularJS macht dies intern, und nachdem ich hier Dokumente und andere Themen gelesen hatte, konnte ich keine konkrete Antwort finden.

Ich würde mich über Hinweise freuen, wie ich die Suche sofort verzögern kann.

146
braincomb

(Siehe Antwort unten für eine Angular 1.3-Lösung.)

Das Problem hierbei ist, dass die Suche jedes Mal ausgeführt wird, wenn sich das Modell ändert. Dies ist jede Keyup-Aktion für eine Eingabe.

Es gibt sauberere Methoden, dies zu tun. Die einfachste Möglichkeit wäre jedoch, die Bindung so zu ändern, dass in Ihrem Controller eine $ scope -Eigenschaft definiert ist, auf der Ihr Filter ausgeführt wird. Auf diese Weise können Sie steuern, wie oft die Variable $ scope aktualisiert wird. Etwas wie das:

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
Jason Aden

UPDATE

Jetzt ist es einfacher als je zuvor (Angular 1.3). Fügen Sie einfach eine Entprellungsoption für das Modell hinzu.

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

Aktualisierter Plunker:
http://plnkr.co/edit/4V13gK

Dokumentation zu ngModelOptions:
https://docs.angularjs.org/api/ng/directive/ngModelOptions

Alte Methode:

Hier ist eine andere Methode ohne Abhängigkeiten, die über den Winkel selbst hinausgehen.

Sie müssen ein Timeout festlegen und Ihre aktuelle Zeichenfolge mit der vorherigen Version vergleichen. Wenn beide übereinstimmen, wird die Suche ausgeführt.

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

und das geht in deine Sicht:

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

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

Der obligatorische Plunker: http://plnkr.co/dAPmwf

295

In Angular 1.3 würde ich Folgendes tun:

HTML:

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

Regler:

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

Im Grunde sagen Sie eckig, dass Sie myDebouncedFunction () ausführen sollen, wenn sich die msg scope-Variable ändert. Das Attribut ng-model-options = "{debounce: 1000}" stellt sicher, dass msg nur einmal pro Sekunde aktualisiert werden kann.

 <input type="text"
    ng-model ="criteria.searchtext""  
    ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
    class="form-control" 
    placeholder="Search" >

Jetzt können wir die ng-model-options-Entprellung mit der Zeit einstellen. Wenn Unschärfe erforderlich ist, muss das Modell sofort geändert werden. Andernfalls wird beim Speichern ein älterer Wert angezeigt, wenn die Verzögerung nicht abgeschlossen ist. 

10
Ali Adravi

Entprellte/gedrosselte Modellaktualisierungen für anglejs: http://jsfiddle.net/lgersman/vPsGb/3/

In Ihrem Fall müssen Sie nichts anderes tun, als die Direktive im jsfiddle-Code wie folgt zu verwenden:

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

Es ist im Grunde ein kleines Stück Code, bestehend aus einer einzigen Winkeldirektive namens "ng-ampere-debounce", die http://benalman.com/projects/jquery-throttle-debounce-plugin/ verwendet und an jedes angehängt werden kann dom-Element. Die Direktive ordnet die angefügten Ereignishandler neu an, um zu steuern, wann Ereignisse gedrosselt werden.

Sie können es zur Drosselung/Entprellung von * Modellwinkelaktualisierungen * Winkelereignishandler verwenden ng- [event] * Jquery-Ereignisbehandlungsroutinen

Schauen Sie: http://jsfiddle.net/lgersman/vPsGb/3/

Die Direktive wird Teil des Orangevolt Ampere-Frameworks sein ( https://github.com/lgersman/jquery.orangevolt-ampere ). 

6
lgersman

Nur für Benutzer, die hier umgeleitet werden:

Wie in Angular 1.3 eingeführt, können Sie ng-model-options attribute verwenden:

<input 
       id="searchText" 
       type="search" 
       placeholder="live search..." 
       ng-model="searchText"
       ng-model-options="{ debounce: 250 }"
/>
6
Morteza Tourani

Für diejenigen, die keyup/keydown im HTML-Markup verwenden.

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
Vinoth

Ich glaube, der beste Weg, dieses Problem zu lösen, ist Ben Almans Plugin jQuery throttle/debounce . Meines Erachtens besteht keine Notwendigkeit, die Ereignisse jedes einzelnen Felds in Ihrem Formular zu verzögern.

Packen Sie einfach Ihren $ scope ein. $ Watch handling function in $ .debounce wie folgt:

$scope.$watch("searchText", $.debounce(1000, function() {
    console.log($scope.searchText);
}), true);
5
Daniel Popov

Eine andere Lösung besteht darin, dem Modellupdate eine Verzögerungsfunktion hinzuzufügen. Die einfache Direktive scheint einen Trick zu bewirken:

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

Verwendungszweck:

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

Sie verwenden also einfach delayed-model anstelle von ng-model und definieren den gewünschten data-delay.

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

3
dfsq

Warum will jeder eine Uhr benutzen? Sie können auch eine Funktion verwenden:

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

Angular 1.3 wird ng-model-options entprellen, aber bis dahin müssen Sie einen Timer verwenden, wie Josue Ibarra sagte. In seinem Code startet er jedoch bei jedem Tastendruck einen Timer. Außerdem verwendet er setTimeout, wenn in Angular $ timeout oder $ apply am Ende von setTimeout verwendet werden muss.

0
F.A.

Ich denke, der einfachste Weg hier ist, den Json vorzuladen oder einmal auf $dirty zu laden, und dann wird die Filtersuche den Rest erledigen. Dies erspart Ihnen die zusätzlichen HTTP-Aufrufe und ist mit vorab geladenen Daten viel schneller. Erinnerung wird weh tun, aber es lohnt sich.

0
NateNjugush

Ich löste dieses Problem mit einer Direktive, die im Grunde genommen das reale ng-Modell an ein spezielles Attribut bindet, das ich in der Direktive beobachte. Dann aktualisiere ich mit einem Entprelldienst mein Direktiveigenschaftsattribut, sodass der Benutzer die Variable darauf aufpasst er bindet an debounce-model anstelle von 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);
  }
};
});

Verwendungszweck:

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

Und im Controller:

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

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

den $ debounce-Service finden Sie hier: http://jsfiddle.net/Warspawn/6K7Kd/

Inspiriert durch die finallyBind-Direktivehttp://jsfiddle.net/fctZH/12/

0
Ofir D