Joining Firebase Queries in Angularfire2

Update:

The problems that I encountered with empty value fields are related to non-existent keys in my database, so most of the discourse here will not apply to your question. If you're looking for a way to β€œjoin” queries in AngularFire2, the answer below does a great job of this. I am currently using combineLatest instead of forkJoin . For this you need to import 'rxjs/add/observable/combineLatest'; .

I have the following denormalized Firebase structure:

 members -memberid1 -threads -threadid1: true, -threadid3: true -username: "Adam" ... threads -threadid1 -author: memberid3 -quality: 67 -participants -memberid2: true, -memberid3: true ... 

I want to make username in my threads view, which is sorted by quality .

My 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 } }); } 

My component:

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

For some reason, this logs what appears to be unfulfilled observables on the console.

enter image description here

I would like to get these values ​​in the threads property, so I can do this in my view as follows:

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

Updated: console.log for allThreads and thisThread

enter image description here

enter image description here

Updated: subscribed to getUsername ()

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

The result of this is objects with no values:

enter image description here

+5
source share
2 answers

You can create an observable based on getFeaturedThreads that queries members and replaces the values ​​in each property of the participants thread with user names:

 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); }); 

This will give you an observable value that emits every time getFeaturedThreads . However, if usernames change, they will not be re-emitted. If this is important, replace forkJoin with combineLatest and remove the first statement from the composite observable elements.

+4
source

To allow users to join, I wrote a service that caches already loaded users and displays them in the reference data with minimal code. It uses a nested map structure to connect:

 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; }); }); } 

And the UserProvider service looks like this:

 @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); } } 

Here is a complete working example of joins and all templates here

0
source

Source: https://habr.com/ru/post/1263109/


All Articles