2014-12-04 9 views
6

Ich versuche, ein Raster in eckigen, mit serverseitigen Sortierung, serverseitige Paginierung und serverseitige Filterung zu implementieren. Mit ui-grid (unstable) habe ich ui.grid.paging hinzugefügt und alles eingerichtet. Sehr schön, ein großes Lob an die Entwickler.angular ui-grid/ng-grid filterChanged feuern zu häufig

Allerdings .... $ scope.gridApi.core.on.filterChanged feuert für jede Taste gedrückt, so wenn ich nach "Patrick" in der Givename Spalte suche, werden sieben Ereignisse ausgelöst und sieben get-Anfragen treffen meine Server. Schlimmer noch, da es sich um eine große Menge handelt, die ich filtere, ist diese Operation ziemlich teuer und die Ergebnisse überholen sich sogar gegenseitig, so wie der spezifischste Filter das schnellste Ergebnis erzielt und Erfolg auslöst, bevor ein weniger spezifisches Ergebnis verarbeitet wird.

Ich möchte es verlangsamen, wie "Feuer nach Eintritt hat aufgehört". Ich bin ziemlich neu in Javascript und REST, das ist mein erstes Real-World-Projekt. Ich würde wirklich jede Idee schätzen, wie man mit diesem Problem umgeht. Es fühlt sich an wie ein gewöhnliches Szenario, also könnte es einige Standardlösungen oder Best Practices geben, die ich vermisse.

Ihre, Patrick.

Antwort

16

Wenn du gehst „alle Winkel“ will gehen würde ich vorschlagen, zu verwenden, um ein $timeout innerhalb der on.filterChanged Ereignishandler:

if (angular.isDefined($scope.filterTimeout)) { 
    $timeout.cancel($scope.filterTimeout); 
} 
$scope.filterTimeout = $timeout(function() { 
    getPage(); 
}, 500); 

wo 500 die Zeit (in Millisekunden) Sie mögen, dass zwischen jedem on.filterChanged Ereignisse warten, bevor auf den Server und getPage gehen() ist die Funktion, die tatsächlich geht zum Server und ruft die Daten ab.

Vergessen Sie nicht, Ihre $timeout auf der Steuerung ‚zerstören‘ Veranstaltung abzusagen:

$scope.$on("$destroy", function (event) { 
    if (angular.isDefined($scope.filterTimeout)) { 
     $timeout.cancel($scope.filterTimeout); 
    } 
}); 
0

Ich würde empfehlen, dass Sie einen Blick auf reactive-extensions for Javascript werfen. Reactive ist genau für diese Art von Szenarien konzipiert. Es gibt eine Methode namens .Throttle (Millisekunden), die Sie an eine Observable anhängen können, die einen Handler aufrufen wird (um Ihre API zu treffen), nachdem x Millisekunden verstrichen sind, damit der Benutzer nichts eingeben kann. Hier

Rx-Js and angular play well together

ist ein Beispiel von einem meiner Projekte ähnlicher, etwas zu tun:

observeOnScope($scope, 'tag', true) 
      .throttle(1000) 
      .subscribe(function (data) { 
       if (data.newValue) { 
        $http({ 
         url: api.endpoint + 'tag/find', 
         method: 'GET', 
         params: {text: data.newValue} 
        }).then(function (result) { 
         $scope.candidateTags = result.data; 
        }) 
       } 
      }); 

Dieser Code nimmt $scope.tag und verwandelt es in einen beobachtbaren. Die .throttle(1000) bedeutet, dass nach 1 Sekunde $scope.tag keine Änderung der Subskriptionsfunktion aufgerufen wird, wobei (Daten) der neue Wert ist. Danach können Sie Ihre API treffen.

benutzte ich diese typeahead-Lookup von Werten aus meiner API zu tun, damit jedes Mal ein Brief geändert offensichtlich war nicht der Weg, um das Backend schlagen :)

+0

Ich habe die „alle Winkel“ Lösung implementiert, da es keine externen Abhängigkeiten beinhalten wird. Ihre Lösungen klingen jedoch sehr interessant und ich werde sie mir bald ansehen. – user3700169

8

ich es zu tun hatte mit dem gleichen Problem und ich kam mit einer anderen Lösung, die meiner Meinung nach mehr „Angular freundlich“ ist . Ich habe die in Angular 1.3 eingeführte ngModelOptions directive verwendet. Ich ersetzte die Standard-Filtervorlage von uiGrid ("ui-grid/ui-grid-filter") durch eine benutzerdefinierte und konfigurierte die ngModelOptions-Direktive für die Eingabe mit einem Standard-Entprellungswert von 300 ms und 0 ms für die Unschärfe.

Dies ist eine Beispielvorlage, die auf ui-grid 3.0 basiert.5 Original-Vorlage, wo ich auch Standard-CSS-Klassen von Bootstrap-Klassen geändert:

$templateCache.put('ui-grid/ui-grid-filter-custom', 
    "<div class=\"ui-grid-filter-container\" ng-repeat=\"colFilter in col.filters\" ng-class=\"{'ui-grid-filter-cancel-button-hidden' : colFilter.disableCancelFilterButton === true }\">" + 
    "<div ng-if=\"colFilter.type !== 'select'\"><input type=\"text\" class=\"input-sm form-control\" ng-model=\"colFilter.term\" ng-model-options=\"{ debounce : { 'default' : 300, 'blur' : 0 }}\" ng-attr-placeholder=\"{{colFilter.placeholder || ''}}\" aria-label=\"{{colFilter.ariaLabel || aria.defaultFilterLabel}}\"><div role=\"button\" class=\"ui-grid-filter-button\" ng-click=\"removeFilter(colFilter, $index)\" ng-if=\"!colFilter.disableCancelFilterButton\" ng-disabled=\"colFilter.term === undefined || colFilter.term === null || colFilter.term === ''\" ng-show=\"colFilter.term !== undefined && colFilter.term !== null && colFilter.term !== ''\"><i class=\"ui-grid-icon-cancel\" ui-grid-one-bind-aria-label=\"aria.removeFilter\">&nbsp;</i></div></div>" + 
    "<div ng-if=\"colFilter.type === 'select'\"><select class=\"form-control input-sm\" ng-model=\"colFilter.term\" ng-attr-placeholder=\"{{colFilter.placeholder || aria.defaultFilterLabel}}\" aria-label=\"{{colFilter.ariaLabel || ''}}\" ng-options=\"option.value as option.label for option in colFilter.selectOptions\"><option value=\"\"></option></select><div role=\"button\" class=\"ui-grid-filter-button-select\" ng-click=\"removeFilter(colFilter, $index)\" ng-if=\"!colFilter.disableCancelFilterButton\" ng-disabled=\"colFilter.term === undefined || colFilter.term === null || colFilter.term === ''\" ng-show=\"colFilter.term !== undefined && colFilter.term != null\"><i class=\"ui-grid-icon-cancel\" ui-grid-one-bind-aria-label=\"aria.removeFilter\">&nbsp;</i></div></div>" + 
    "</div>" 
); 

Der letzte Schritt für diese Arbeit ist diese Vorlage in jedem columnDef einstellen, wo Sie Filterung aktivieren:

columnDefs: [{ 
    name: 'theName', 
    displayName: 'Whatever', 
    filterHeaderTemplate: 'ui-grid/ui-grid-filter-custom' 
}] 

Leider Ich konnte keine Möglichkeit finden, diese Vorlage global zu definieren, also musste ich filterHeaderTemplate überall wiederholen ... Das ist der einzige Nachteil, aber auf der anderen Seite können Sie bei Bedarf auch weitere Filter zu Ihrer benutzerdefinierten Vorlage hinzufügen.

Hoffe es hilft!

+1

Das ist eigentlich die beste Antwort. – uksz

+0

tolle Lösung. Danke – WeMakeSoftware

+1

Eigentlich, wenn Sie die Überschreibung in app.run mit $ templateCache.put setzen ('ui-grid/ui-grid-filter', ...); Dann sollte dies die Standarddefinition von ui-grid überschreiben. – anre

0

JavaScript SetTimeout Funktion kann auch für das Geben Verzögerung wie unten angegeben verwendet werden.

$scope.gridApi.core.on.filterChanged($scope, function() { 

    if (angular.isDefined($scope.filterTimeout)) { 
     clearTimeout($scope.filterTimeout); 
    } 
    $scope.filterTimeout = setTimeout(function(){ 
     getPage(); 
    },1000); 

});