2014-08-29 1 views
7

Ich bin ziemlich neu in Angular, also frage ich vielleicht das Unmögliche, aber trotzdem, hier ist meine Herausforderung.Eine Lösung für das Streaming von JSON mit oboe.js in AngularJS?

Da unser Server JSON-Daten nicht paginieren kann, möchte ich den JSON streamen und ihn Seite für Seite zum Modell des Controllers hinzufügen. Der Benutzer muss nicht auf das Laden des gesamten Streams warten, daher aktualisiere ich die Ansicht für alle X-Datensätze (Seitengröße).

Ich habe oboe.js zum Parsen des JSON-Streams gefunden und mit Bower zu meinem Projekt hinzugefügt. (Bower installieren Oboe - Save).

Ich möchte das Controller-Modell während des Streaming aktualisieren. Ich habe die $ q-Implementierung von Pomises nicht verwendet, da nur eine .resolve (...) möglich ist und ich möchte, dass mehrere Seiten über den Stream geladen werden, so dass der $ digest mit jeder Seite aufgerufen werden muss. Der erholsamen Service, der genannt wird, ist/service/Aufgaben/Suche

ich eine Fabrik mit einer Suchfunktion erstellt, die ich aus dem Controller nennen:

'use strict'; 

angular.module('myStreamingApp') 
    .factory('Stream', function() { 
     return { 
      search: function(schema, scope) { 
       var loaded = 0; 
       var pagesize = 100; 
       // JSON streaming parser oboe.js 
       oboe({ 
        url: '/service/' + schema + '/search' 
       }) 
         // process every node which has a schema 
         .node('{schema}', function(rec) { 
          // push the record to the model data 
          scope.data.push(rec); 
          loaded++; 
          // if there is another page received then refresh the view 
          if (loaded % pagesize === 0) { 
           scope.$digest(); 
          } 
         }) 
         .fail(function(err) { 
          console.log('streaming error' + err.thrown ? (err.thrown.message):''); 
         }) 
         .done(function() { 
          scope.$digest(); 
         }); 
      } 
     }; 
    }); 

Mein Controller:

'use strict'; 
angular.module('myStreamingApp') 
    .controller('MyCtrl', function($scope, Stream) { 
     $scope.data = []; 
     Stream.search('tasks', $scope); 
    }); 

Es scheint alles zu funktionieren. Nach einer Weile wird das System jedoch langsam und der http-Aufruf wird nach der Aktualisierung des Browsers nicht beendet. Auch der Browser (Chrome) stürzt ab, wenn zu viele Datensätze geladen sind. Vielleicht bin ich auf dem Holzweg, weil das Überschreiten des Bereichs auf die Fabriksuchfunktion nicht richtig funktioniert und ich vermute, dass das Aufrufen des $ digest auf diesem Bereich mir Probleme bereitet. Irgendwelche Ideen zu diesem Thema sind willkommen. Vor allem, wenn Sie eine Idee haben, auf deren Umsetzung, wo die Fabrik (oder Service) ein Versprechen zurückkehren konnte und ich

$scope.data = Stream.search('tasks'); 

in der Steuerung nutzen könnten.

Antwort

14

Ich graben ein bisschen weiter und kam mit der folgenden Lösung. Es könnte jemandem helfen:

Die Fabrik (benannt Stream) hat eine Suchfunktion, die Parameter für die Ajax-Anfrage und eine Rückruffunktion übergeben wird. Der Rückruf wird für jede Seite der vom Stream geladenen Daten aufgerufen. Die Callback-Funktion wird über eine deferred.promise aufgerufen, so dass der Bereich automatisch mit jeder Seite aktualisiert werden kann. Um auf die Suchfunktion zuzugreifen, benutze ich einen Dienst (namens Suche), der zunächst einen leeren Array von Daten zurückgibt. Wenn der Strom fortschreitet, ruft die Fabrik die vom Dienst übergebene Rückruffunktion auf, und die Seite wird zu den Daten hinzugefügt.

Ich kann jetzt das Suchdienstformular innerhalb eines Controllers aufrufen und den Rückgabewert dem Bereichsdatenarray zuweisen.

Der Service und die Fabrik:

'use strict'; 
angular.module('myStreamingApp') 
     .service('Search', function(Stream) { 
      return function(params) { 
       // initialize the data 
       var data = []; 
       // add the data page by page using a stream 
       Stream.search(params, function(page) { 
        // a page of records is received. 
        // add each record to the data 
        _.each(page, function(record) { 
         data.push(record); 
        }); 
       }); 
       return data; 
      }; 
     }) 
     .factory('Stream', function($q) { 
      return { 
       // the search function calls the oboe module to get the JSON data in a stream 
       search: function(params, callback) { 
        // the defer will be resolved immediately 
        var defer = $q.defer(); 
        var promise = defer.promise; 
        // counter for the received records 
        var counter = 0; 
        // I use an arbitrary page size. 
        var pagesize = 100; 
        // initialize the page of records 
        var page = []; 
        // call the oboe unction to start the stream 
        oboe({ 
         url: '/api/' + params.schema + '/search', 
         method: 'GET' 
        }) 
          // once the stream starts we can resolve the defer. 
          .start(function() { 
           defer.resolve(); 
          }) 
          // for every node containing an _id 
          .node('{_id}', function(node) { 
           // we push the node to the page 
           page.push(node); 
           counter++; 
           // if the pagesize is reached return the page using the promise 
           if (counter % pagesize === 0) { 
            promise.then(callback(page)); 
            // initialize the page 
            page = []; 
           } 
          }) 
          .done(function() { 
           // when the stream is done make surethe last page of nodes is returned 
           promise.then(callback(page)); 
          }); 
        return promise; 
       } 
      }; 
     }); 

Jetzt kann ich den Dienst innerhalb einer Controller anrufen und die Antwort des Service auf den Umfang zuweisen:

$scope.mydata = Search({schema: 'tasks'}); 

-Update 30. August 2014

Ich habe ein Winkel-Oboe-Modul mit der oben genannten Lösung ein bisschen mehr strukturiert erstellt. https://github.com/RonB/angular-oboe

Verwandte Themen