2017-03-06 5 views
1

Ich versuche, alle "Übungen" eines Benutzers zu zeigen. Da ein "Übungs" -Objekt zu mehreren Benutzern gehören kann, ist dies eine typische Viele-zu-Viele-Beziehung. Da die Dokumente vorschlagen, die Struktur der Datenbank zu reduzieren, hat jeder Benutzer ein Wörterbuch von { $exercise_key: true }.Wie erstelle ich eine ObservableList aus einer Liste von Schlüsseln?

Nach Stunden der Suche, habe ich endlich etwas, das funktioniert, aber es ist hässlich und scheint ziemlich langsam und ineffizient. Ich erhalte zuerst eine beobachtbare Liste der Übungsschlüssel vom Benutzer, dann erhalte ich für jeden dieser Schlüssel ein FirebaseObservableObject, das ich als ein "Metadaten" -Attribut anhefte.

Wenn ich die Seite lade, sehe ich zuerst eine Liste von leeren Kugeln, dann bekommen nur die Hälfte der Kugeln ihren Text, dann endlich der Rest. Das Ganze dauert ca. 2 Sekunden. Dieses "Flimmern" tritt auch auf, wenn ich dynamisch eine neue Übung hinzufüge, ohne die Seite neu zu laden. Bitte versuchen Sie es selbst auf https://papa-bear-1f486.firebaseapp.com (google auth verwenden).

Meine Datenbank sieht wie folgt aus:

{ 
    "exercises" : { 
    "-KeVLI3UbGkRbifLffMa" : { 
     "createdAt" : 1488748884471, 
     "name" : "squat1" 
    }, 
    "-KeVLISTWaXbFxS6yqfv" : { 
     "createdAt" : 1488748886066, 
     "name" : "squat2" 
    }, 
    }, 
    "users" : { 
    "JtLTkOb8EXTeCeasF6vLpIpoUDB2" : { 
     "exercises" : { 
     "-KeVLI3UbGkRbifLffMa" : true, 
     "-KeVLISTWaXbFxS6yqfv" : true, 
     } 
    } 
    } 
} 

Dies ist der hässliche Code kam ich mit:

private exercisesKey$: FirebaseListObservable<any[]>; 
private exercises$: Observable<any[]>; 

constructor(private af: AngularFire, private auth: AuthService) { 
    const path = '/users/' + auth.id + '/exercises'; 

    this.exercisesKey$ = af.database.list(path); 
    this.exercises$ = this.exercisesKey$.map((items) => { 
    items.map(item => { 
     item.metadata = this.getExercise(item.$key); 
     return item; 
    }); 
    return items; 
}); 

getExercise(key: string): FirebaseObjectObservable<IExercise> { 
    return this.af.database.object('/exercises/' + key); 
} 

Und meiner Meinung nach:

<ul> 
    <li *ngFor="let exercise of exerciseService.exercises$ | async"> 
    {{ (exercise.metadata | async)?.name }} 
    </li> 
</ul> 

Also ich denke, meine Frage ist: Wie bekomme ich eine beobachtbare Liste von Objekten aus einer Liste von Schlüsseln erstellt?

package.json:

"angularfire2": "^2.0.0-beta.5", 
"firebase": "^3.5.2", 
+0

Sie sollten Ihre AngularFire2-Abhängigkeit aktualisieren, da es seit der Version, die Sie verwenden, zahlreiche Fehlerkorrekturen gegeben haben. – cartant

+0

Zusätzlich zu den Kommentaren von Cartant sollte es nicht notwendig sein, die asynchrone Pipe sowohl in Ihrem ngFor-Ausdruck als auch in Ihrem interpolierten Wert zu verwenden. Mein Verständnis ist, dass Sie es in diesem Fall nur im ngFor-Ausdruck verwenden würden. –

Antwort

0

Es gibt nichts, was Sie tun können, eine Abfrage für jede Übung zu vermeiden (es sei denn Sie eine einzelne Abfrage ausführen wollen, die jede Übung abruft). Sie können jedoch die Dinge ein wenig vereinfachen.

Sie können combineLatest verwenden, um eine beobachtbare von Übungen zu komponieren:

import 'rxjs/add/observable/combineLatest'; 
import 'rxjs/add/observable/of'; 
import 'rxjs/add/operator/switchMap'; 

constructor(private af: AngularFire, private auth: AuthService) { 
    const path = '/users/' + auth.id + '/exercises'; 
    this.exercisesKey$ = af.database.list(path); 
    this.exercises$ = this.exercisesKey$ 
    .switchMap((items) => items.length === 0 ? 
     Observable.of([]) : 
     Observable.combineLatest(...items.map(item => this.getExercise(item.$key))); 
    ); 
}); 

Wenn die Liste der Übung Tasten ändert oder wenn eine Übung selbst ändert, wird die zusammengesetzt beobachtbare die Liste der Übungen wieder emittieren.

Wenn Sie nur eine Momentaufnahme der Übungen wollen und wollen nicht eine beobachtbare dass wieder abgibt, wenn eine Übung ändert, verwenden forkJoin statt combineLatest und verwenden first jede Übung zu vervollständigen beobachtbar:

import 'rxjs/add/observable/forkJoin'; 
import 'rxjs/add/observable/of'; 
import 'rxjs/add/operator/first'; 
import 'rxjs/add/operator/switchMap'; 

constructor(private af: AngularFire, private auth: AuthService) { 
    const path = '/users/' + auth.id + '/exercises'; 
    this.exercisesKey$ = af.database.list(path); 
    this.exercises$ = this.exercisesKey$ 
    .switchMap((items) => items.length === 0 ? 
     Observable.of([]) : 
     Observable.forkJoin(...items.map(item => this.getExercise(item.$key).first())); 
    ); 
}); 

In beiden Fällen würde Ihre Vorlage in etwa so aussehen:

<ul> 
    <li *ngFor="let exercise of exerciseService.exercises$ | async"> 
    {{ exercise.name }} 
    </li> 
</ul> 
+0

Danke @cartant, sehr hilfreich! – user1607256

Verwandte Themen