2016-08-17 1 views
0

Ich versuche, eine Angular-Anwendung zu entwerfen, die über Komponenten verfügt, die eine Rückruffunktion und ein "Datenanforderungsobjekt" mit einem Angular-Dienst registrieren. Dieser Service verfolgt im Wesentlichen alle Datenanforderungsobjekte und auf welche Callback-Funktionen sie sich beziehen. Dann führt es eine lange Abfrage durch, um asynchrone Aufrufe an eine REST-konforme API auszuführen. Wenn alle Daten eintreffen, entscheidet der Dienst, welche Komponenten welche Daten benötigen und ruft diese mit den Ergebnissen des API-Aufrufs auf.Anwendung mit mehreren API-Datendiensten

Das Problem, das ich habe Probleme mit meinem Kopf herum ist, dass jede Komponente die Daten möglicherweise in ein JSON-Objekt umgewandelt werden muss, das ein bestimmtes Format vorschreibt. Beispielsweise kann eine Diagrammkomponente erfordern, dass das Datenergebnis in einer Richtung aussieht, aber eine Tabellenkomponente, die möglicherweise ein anderes Aussehen des Datenergebnisses erfordert.

Um die Sache noch komplizierter zu machen, möchte ich meinen Datendienst so gestalten, dass die Komponente Daten aus mehreren verschiedenen RESTful-APIs registrieren kann.

Da ich Angular ziemlich neu bin, wollte ich Hilfe bei einigen Best Practices für die Durchführung dieser Art von Anwendung bekommen. Was ich vorhabe, ist ein "primärer" Datendienst, bei dem sich meine Komponenten registrieren. Die Registrierungsfunktion nimmt als Argument eine Rückruffunktion und eine Datenanforderung Objekt, das wie so in einem Format sein wird:

{ 
    "service": "apiService", 
    "request": { 
     ... 
    } 
} 

Dann wird es ein separater Angular Service für jeden RESTful API sein. Diese Unterdienste behandeln, was mit dem Datenanforderungsobjekt zu tun ist und entsprechen dem obigen "Dienst" -Feld. Der primäre Datendienst stellt dann Anfragen an die Unterdienste in einem langen Abfragezyklus in eine Warteschlange. So ist die primäre Datendienst wird so etwas wie folgt aussehen (Anmerkung: Ich verwende ES6):

class DataService { 
    constructor($q, $interval, $injector) { 
    this.$q = $q; 
    this.$interval = $interval; 
    this.$injector = $injector; 

    this.subscriptions = []; 
    this.callbacks = []; 

    this.poll(); 
    } 

    poll() { 
    let reqs = []; 
    angular.forEach(this.subscriptions, (sub, i) => { 
     let service = this.$injector.get(sub.service); 
     let deferred = this.$q.defer(); 

     reqs.push(deferred); 
     service.get(sub.request).then((result) => { 
      this.callbacks[i](result); 
      deferred.resolve(result); 
     }, (result) => { 
      deferred.reject(result); 
     }); 
    }); 

    this.$q.all(reqs).then(() => { 
     this.$interval(poll, this.pollInterval || 10000); 
    }); 
    } 

    register(request, callback) { 
    this.subscriptions.push(request); 
    this.callbacks.push(callback); 
    } 
} 

angular.module('DataService', []).service('DataService', DataService); 

Das Stück, das Ich habe Probleme, herauszufinden, wie zu implementieren, ist die ‚Datentransformation‘ Stück. Soweit ich das beurteilen kann, gibt es eigentlich nur zwei Orte, die ich kann sehen, wo verwandeln diese Daten stattfinden kann:

Der erste Weg doesn

  1. Innerhalb der Komponente
  2. Innerhalb der einzelnen API-Dienste Für mich scheint das keine gangbare Option zu sein, denn es bricht die gängige Praxis, dass Komponenten etwas "dumm" sein sollten. Die Komponente sollte die Transformation der Daten, die die RESTful-API zurückgibt, nicht verarbeiten. Sie sollte nur die Daten so verwenden, wie sie sind.

    Aber die zweite Möglichkeit wirft auch ein anderes Problem auf, nämlich dass jeder RESTful-API-Dienst Transformationsfunktionen für jeden von mir erstellten Komponententyp haben müsste. Das scheint mir irgendwie nicht "sauber" zu sein.

    Gibt es eine andere Möglichkeit, meine Anwendung zu entwerfen, um dieses Ziel zu erreichen? Jeder Einblick würde geschätzt werden.

Antwort

0

Nur ein Vorschlag: Verwenden Sie das integrierte Event-System von eckig.

//Constructor 
constructor($q, $interval, $injector, $rootScope) { 
this.$q = $q; 
this.$interval = $interval; 
this.$injector = $injector; 
this.$rootScope = $rootScope; 
this.listeners = []; 
//No idea how its written in Angular2 but cleanup the event listeners when this is destroyed 
//Example in 1.5.x: 
//$scope.$on('$destroy',() => angular.forEach(this.listeners, l => l()); 
this.poll(); 
} 

//The new register method 
register(ev, callback) { 
//When cleaning up - iterate over listeners and invoke each destroy function - see constructor 
this.listeners.push(this.$rootScope.$on(ev, callback)); 
} 

//The new poll method 
poll() { 
    let reqs = []; 
    angular.forEach(this.subscriptions, (sub, i) => { 
     let service = this.$injector.get(sub.service); 
     let deferred = service.get(sub.request).then((response) => { 
     //responseToEventsMapper should map and parse response to list of events to fire. 
     //Lets say the response is an authentication response for login attempt, 
     //then the triggered events will be 'Auth:stateChange' and 'Auth:login' with 
     //response.user as data. configure responseToEventsMapper upfront. 
     let events = this.responseToEventsMapper(response); 
     angular.forEach(events, event => this.$rootScope.$emit(event.type, event.data)); 
     }); 
     reqs.push(deferred); 
    }); 

    this.$q.all(reqs).then(() => { 
     this.$interval(poll, this.pollInterval || 10000); 
    }); 
    } 
+0

Danke für den Tipp. Ich habe nicht bemerkt, dass Sie $ emit und $ auf diese Weise verwenden können (oder dass $ on eine Deregistrierungsfunktion zurückgibt, besonders praktisch). Für den responseToEventsMapper ist meine Frage immer noch, ob der Mapper im Service oder in der Komponente definiert werden sollte? Angenommen, ich habe zwei APIs, apiA und apiB. Beide geben Antworten in völlig anderen Formaten zurück, jsonA und jsonB. Die Komponente benötigt jedoch Daten in einem anderen Format, jsonC.responseToEventsMapper sollte diese Umwandlung durchführen, aber wo ist ein geeigneter Ort für die Definition? – snusnu

+0

Sie können jedem Teilnehmer während der Registrierungsphase einen eigenen Mapper bereitstellen. –

Verwandte Themen