2016-11-24 7 views
0

Angular2-Komponente zeigt ein Objekt an. Zusätzlich zu den Basisobjekt-Informationen müssen zusätzliche Daten über einen Dienst geladen werden, der Observable zurückgibt und ziemlich langsam ist.Laden von langsamen Unterressourcen in Angular2

Basisteil des Objekts Informationen sollten sofort und die zusätzlichen Daten einmal vorhanden angezeigt werden.

Etwas wie folgt:

SomeObjectComponent { 

    someObject: SomeObject; 
    extraData: ExtraData; 

    @Input() 
    set item(v: SomeObject) { 
     this.someObject = v; 
     backendService.getExtraData(v.id) 
      .subscribe(d => this.extraData = d); 
    }   

} 

// in the template 
<some-object [item]="currentlySelected"></some-object> 

Beim Laden langsam und Benutzer navigiert zwischen verschiedenen Werten von SomeObject kann asynchroner Laden laden und zu falschen extraData für den aktuellen Eintrag zuweisen.

Was wäre der beste Weg, dies zu lösen? Was passiert, wenn jedes Mal mehrere zusätzliche Datenelemente geladen werden müssen (und jedes, sobald es geladen wird)?

+0

Ich verstehe das Problem nicht vollständig. Wenn Sie also navigieren, wird die gleiche Komponente geladen und verschiedene "Items" übergeben. Ich nehme an, Sie möchten eine Anfrage abbrechen, wenn die Daten bei der Navigation zu einem anderen Artikel noch nicht angekommen sind. Was, wenn Sie zurück navigieren? Ich nehme an, Sie möchten das Objekt aus einem Cache holen (aus der vorherigen Anfrage). Ich würde einen Service erstellen, der sicherstellt, dass zuvor geladene Artikel sofort verfügbar sind. Zum Beispiel wie in http://stackoverflow.com/questions/36271899/what-is-the-correct-way-to-share-the-result-of-an-angular-2-http-network-call-in gezeigt –

+0

Cache löst das ursprüngliche Problem nicht. Nur eine einzige SomeObjectComponent-Instanz wird hier initiiert und dann ändert sich der Wert der Eigenschaft "currentSelected" des Containers und wird über die [item] -Bindung weitergegeben. Eine Lösung würde Subskription speichern und abbrechen, eine andere nach "this.someObject === v" innerhalb von subscribe suchen. Ich wollte wissen, welche Optionen und welche die besten sind, vor allem, wenn wir mehrere zusätzliche Datenelemente haben. Gibt es hier eine magische RxJS-Lösung? –

+0

Ok, das macht es klarer.Cache löst das ursprüngliche Problem nicht, aber ich war neugierig, ob dies auch eine Voraussetzung ist. –

Antwort

1

Wenn ich Sie richtig verstanden hat, im Grunde, was Sie suchen ist ein Weg, „alte“ Rest-Anrufe abzubrechen, sobald ein neues Element ausgewählt ist -> der rxjs-Weg wäre ein .takeUntil(...)

zu nutzen
SomeObjectComponent implements OnDestroy { 
    // to clean up any open subscriptions it is helpful to have a destroy-event 
    private destroyed$: Subject<any> = new Subject<any>(); 

    // your vars 
    someObject: SomeObject; 
    extraData: ExtraData; 

    @Input() 
    set item(v: SomeObject) { 
     this.someObject = v; 
     this.loadExtraData$.next(v.id); 
    }   

    // a trigger to load extra data 
    private loadExtraData$: Subject<number> = new Subject<number>(); // type "number" is just an assumption, this can be whatever type "v.id" is 

    // the stream to load extra data 
    private extraData$: Observable<any> = this.loadExtraData$ 
     .takeUntil(this.destroyed$) // when the component is destroyed this will ensure that any open subscription will be closed as well to prevent memory leaks 
     .switchMap(id => { 
      return this.backendService 
       .getExtraData(id) 
       .takeUntil(this.loadExtraData$); // will discard the rest-call the next time loadExtraData$ is triggered (if it is still running) 
     }) 
     .do(extraData => this.extraData = extraData) // if you only need the data in the template, then don't use this line and use the "| async"-pipe instead 
     .share(); 

    // this is called by the angular2-lifecycle, when the component is removed 
    ngOnDestroy() { 
     this.destroyed$.next(); 
    } 
} 

Wenn Sie nur die zusätzlichen Daten in der Vorlage benötigen, dann die async -pipe verwenden möchten Sie vielleicht:

<span>Some extra data: {{(extraData$ | async)?.someAttribute}}</span> 

Als kleine Randnotiz und sl ightly Wegthema: Es ist eine bessere Praxis zu nicht Belastung der zusätzlichen Daten innerhalb der Komponente, aber die Komponente außerhalb und ein @Input() für die Extra-Daten, für eine Reihe von Gründen verwenden:

  • Testbarkeit ist viel effizienter und leichter zu erreichen, wenn die Komponenten und nicht verlassen sich auf Wartungs- oder anderen Komponenten
  • vor allem mit Rest-Anrufe gibt es eine Gefahr, macht redundante Anfragen, wenn eine Komponente zweimal zB zugegeben gekapselt
  • Komponenten sind viel einfacher in verschiedenen Kontexten wiederverwendet werden, wenn sie und

s @Output() nur @Input() s haben aber sagen, dass ohne Ihre aktuelle Architektur zu kennen, diese größere Refactoring tragen könnte, die nicht die Zeit wert sein könnte, aber es ist etwas für zukünftige Anwendungen im Auge zu behalten.

Verwandte Themen