5

Update:

Die Fragen, die ich mit den leeren Wertfeldern wurde die Begegnung hatte mit nicht vorhandenen Schlüsseln in meiner Datenbank zu tun, so dass die meisten des Diskurses werden hier nicht auf Ihre Anwendung Frage. Wenn Sie nach einer Möglichkeit suchen, Abfragen in AngularFire2 zu "verbinden", ist die untenstehende Antwort eine gute Sache. Ich verwende derzeit combineLatest anstelle von forkJoin. Um dies zu tun, müssen Sie import 'rxjs/add/observable/combineLatest';.‚Joining‘ Firebase Abfragen in Angularfire2

Ich habe folgende normalisierte Struktur Firebase:

members 
    -memberid1 
    -threads 
     -threadid1: true, 
     -threadid3: true 
    -username: "Adam" 
    ... 

threads 
    -threadid1 
     -author: memberid3 
     -quality: 67 
     -participants 
     -memberid2: true, 
     -memberid3: true 
    ... 

möchte ich username in meiner threads Ansicht machen, die von quality sortiert ist.

Mein Service:

getUsername(memberKey: string) { 
    return this.af.database.object('/members/' + memberKey + '/username') 
} 

getFeaturedThreads(): FirebaseListObservable<any[]> { 
    return this.af.database.list('/threads', { 
     query: { 
      orderByChild: 'quality', 
      endAt: 10 
     } 
    }); 
} 

Meine Komponente:

ngOnInit() { 
    this.threads = this.featuredThreadsService.getFeaturedThreads() 
    this.threads.subscribe( 
     allThreads => 
     allThreads.forEach(thisThread => { 
      thisThread.username = this.featuredThreadsService.getUsername(thisThread.author) 
      console.log(thisThread.username) 
     }) 
    ) 
} 

Aus irgendeinem Grund diese protokolliert, was die Konsole wie unerfüllt Observablen aussieht.

enter image description here

Ich möchte diese Werte in einer Eigenschaft von threads bekommen, damit ich es in meinen Augen wie diese machen:

<div *ngFor="let thread of threads | async" class="thread-tile"> 
    ... 
    {{threads.username}} 
    ... 
</div> 

Aktualisiert: console.log für allThreads und thisThread

enter image description here

enter image description here

Aktualisiert: bis getUsername() gezeichnet

this.featuredThreadsService.getUsername(thisThread.author) 
     .subscribe(username => console.log(username)) 

Das Ergebnis hiervon ist, Objekte ohne Werte:

enter image description here

+0

Ihre 'getUsername' Methode gibt nichts zurück. Ist das beabsichtigt? – echonax

+0

Entschuldigung. Das ist ein Überbleibsel einer anderen Implementierung. Ich habe es geändert. Die aktuelle Methode verhält sich so, wie ich sie in meiner Frage beschrieben habe. –

+0

Kannst du console.log für 'allThreads' und' thisThread' setzen, um sicherzustellen, dass sie definiert sind? – echonax

Antwort

4

Sie eine beobachtbare auf getFeaturedThreads Basis zusammenstellen können, die abfragt Mitglieder und ersetzt die Werte in participants Eigenschaft des Threads mit Benutzernamen:

import { Observable } from 'rxjs/Observable'; 
import 'rxjs/add/observable/forkJoin'; 
import 'rxjs/add/operator/do'; 
import 'rxjs/add/operator/first'; 
import 'rxjs/add/operator/switchMap'; 

let featuredThreadsWithUserNames = this.getFeaturedThreads() 

    // Each time the getFeaturedThreads emits, switch to unsubscribe/ignore 
    // any pending member queries: 

    .switchMap(threads => { 

    // Map the threads to the array of observables that are to be 
    // joined. When the observables emit a value, update the thread. 

    let memberObservables = []; 
    threads.forEach(thread => { 

     // Add the author: 

     memberObservables.push(this.af.database 
     .object(`members/${thread.author}`) 
     .first() 
     .do(value => { thread.author = value.username; }) 
    ); 

     // Add the participants: 

     Object.keys(thread.participants).forEach(key => { 
     memberObservables.push(this.af.database 
      .object(`members/${key}`) 
      .first() 
      .do(value => { thread.participants[key] = value.username; }) 
     ); 
     }); 
    }); 

    // Join the member observables and use the result selector to 
    // return the threads - which will have been updated. 

    return Observable.forkJoin(...memberObservables,() => threads); 
    }); 

Dies wird Ihnen eine Observable, die jedes Mal emittiert emittiert getFeaturedThreads emittiert. Wenn sich die Benutzernamen ändern, wird sie jedoch nicht erneut ausgegeben. Wenn das wichtig ist, ersetzen Sie forkJoin durch combineLatest, und entfernen Sie den first-Operator aus den zusammengesetzten Mitglied Observables.

+0

Danke, ich werde an dieser Implementierung arbeiten Ich weiß nicht, ob es wichtig ist, noch darauf hinzuweisen, dass 'Teilnehmer' nicht die gleichen sind wie' Autor' In diesem Fall habe ich den Schlüssel des Mitglieds verfügbar für mich in der 'author' Eigenschaft des thread.Außerdem sehe ich' $ {foo} 'im Pfad, aber es gibt nichts darüber in der Dokumentation. Ist das nur eine Kurzform für' ('/ path' + foo) '? –

+2

Sie sind ES6 Vorlagenliterale: http: // stackoverflo w.com/questions/27565056/es6-template-literals-vs-concatenated-strings – cartant

+0

Ich musste einige Klammerausgaben in diesem beheben. Ich denke, dass ich die richtigen losgeworden bin ... Ich habe den 'Teilnehmer'-Block entfernt, um mich gerade auf den 'Autor'-Benutzernamen zu konzentrieren. Wenn ich 'memberObservables' an der Konsole nach dem' forEach'-Block protokolliere, lande ich aus irgendeinem Grund mit einem Array leerer Observables. Ich weiß, dass dies eine potentiell dumme Aussage ist, denn das mag der Punkt sein, aber ich nehme an, ich erwarte, dass der forEach-Block dieses Array auffüllt. Ich gebe Ihnen die Gelegenheit, dies vor meiner letzten Frage zu dieser Lösung zu beantworten. –

0

Um Joins für Benutzer zu lösen, habe ich einen Dienst geschrieben, der die bereits abgerufenen Benutzer zwischenspeichert und sie mit minimalem Code in die referenzierenden Daten abbildet.Es verwendet eine verschachtelte Map-Struktur der Verbindung zu tun:

constructor(public db: AngularFireDatabase, public users:UserProvider) { 
    this.threads = db.list('threads').valueChanges().map(messages => { 
     return threads.map((t:Message) => { 
     t.user = users.load(t.userid); 
     return m; 
     }); 
    }); 
} 

Und der UserProvider Service sieht so aus:

@Injectable() 
export class UserProvider { 
    db: AngularFireDatabase; 
    users: Map<String, Observable<User>>; 

    constructor(db: AngularFireDatabase) { 
    this.db = db; 
    this.users = new Map(); 
    } 

    load(userid:string) : Observable<User> { 
    if(!this.users.has(userid)) { 
     this.users.set(userid, this.db.object(`members/${userid}`).valueChanges()); 
    } 
    return this.users.get(userid); 
    } 
} 

Es gibt ein komplettes Arbeitsbeispiel für die Verbindungen und alle vorformulierten here