'Joining' Angularfire2 中的 Firebase 查询
'Joining' Firebase Queries in Angularfire2
更新:
我遇到的空值字段问题与数据库中不存在的键有关,因此此处的大部分讨论不适用于您的问题。如果您正在寻找一种在 AngularFire2 中进行 'join' 查询的方法,下面接受的答案可以很好地完成这项工作。我目前使用 combineLatest
而不是 forkJoin
。为此,您必须 import 'rxjs/add/observable/combineLatest';
.
我有以下非规范化的 Firebase 结构:
members
-memberid1
-threads
-threadid1: true,
-threadid3: true
-username: "Adam"
...
threads
-threadid1
-author: memberid3
-quality: 67
-participants
-memberid2: true,
-memberid3: true
...
我想在按 quality
排序的 threads
视图中呈现 username
。
我的服务:
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
}
});
}
我的组件:
ngOnInit() {
this.threads = this.featuredThreadsService.getFeaturedThreads()
this.threads.subscribe(
allThreads =>
allThreads.forEach(thisThread => {
thisThread.username = this.featuredThreadsService.getUsername(thisThread.author)
console.log(thisThread.username)
})
)
}
出于某种原因,这会将看似未完成的可观察对象记录到控制台。
我想将这些值放入 threads
的 属性 中,这样我就可以像这样在我的视图中呈现它:
<div *ngFor="let thread of threads | async" class="thread-tile">
...
{{threads.username}}
...
</div>
已更新:console.log
allThreads
和 thisThread
已更新:订阅了 getUsername()
this.featuredThreadsService.getUsername(thisThread.author)
.subscribe( username => console.log(username))
结果是没有值的对象:
您可以基于 getFeaturedThreads
编写一个可观察对象,查询成员并将每个线程的 participants
属性 中的值替换为用户名:
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);
});
这将为您提供一个在每次 getFeaturedThreads
发射时发射的可观察对象。但是,如果用户名发生变化,则不会 re-emit。如果这很重要,请将 forkJoin
替换为 combineLatest
并从组合成员可观察对象中删除 first
运算符。
为了解决用户连接问题,我编写了一个服务来缓存已经获取的用户,并用最少的代码将它们映射到引用数据中。它使用嵌套映射结构进行连接:
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;
});
});
}
UserProvider 服务如下所示:
@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
更新:
我遇到的空值字段问题与数据库中不存在的键有关,因此此处的大部分讨论不适用于您的问题。如果您正在寻找一种在 AngularFire2 中进行 'join' 查询的方法,下面接受的答案可以很好地完成这项工作。我目前使用 combineLatest
而不是 forkJoin
。为此,您必须 import 'rxjs/add/observable/combineLatest';
.
我有以下非规范化的 Firebase 结构:
members
-memberid1
-threads
-threadid1: true,
-threadid3: true
-username: "Adam"
...
threads
-threadid1
-author: memberid3
-quality: 67
-participants
-memberid2: true,
-memberid3: true
...
我想在按 quality
排序的 threads
视图中呈现 username
。
我的服务:
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
}
});
}
我的组件:
ngOnInit() {
this.threads = this.featuredThreadsService.getFeaturedThreads()
this.threads.subscribe(
allThreads =>
allThreads.forEach(thisThread => {
thisThread.username = this.featuredThreadsService.getUsername(thisThread.author)
console.log(thisThread.username)
})
)
}
出于某种原因,这会将看似未完成的可观察对象记录到控制台。
我想将这些值放入 threads
的 属性 中,这样我就可以像这样在我的视图中呈现它:
<div *ngFor="let thread of threads | async" class="thread-tile">
...
{{threads.username}}
...
</div>
已更新:console.log
allThreads
和 thisThread
已更新:订阅了 getUsername()
this.featuredThreadsService.getUsername(thisThread.author)
.subscribe( username => console.log(username))
结果是没有值的对象:
您可以基于 getFeaturedThreads
编写一个可观察对象,查询成员并将每个线程的 participants
属性 中的值替换为用户名:
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);
});
这将为您提供一个在每次 getFeaturedThreads
发射时发射的可观察对象。但是,如果用户名发生变化,则不会 re-emit。如果这很重要,请将 forkJoin
替换为 combineLatest
并从组合成员可观察对象中删除 first
运算符。
为了解决用户连接问题,我编写了一个服务来缓存已经获取的用户,并用最少的代码将它们映射到引用数据中。它使用嵌套映射结构进行连接:
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;
});
});
}
UserProvider 服务如下所示:
@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