【问题标题】:'Joining' Firebase Queries in Angularfire2在 Angularfire2 中“加入”Firebase 查询
【发布时间】:2017-06-05 18:42:24
【问题描述】:

更新:

我遇到的空值字段问题与我的数据库中不存在的键有关,因此这里的大部分内容都不适用于您的问题。如果您正在寻找一种在 AngularFire2 中“加入”查询的方法,那么下面接受的答案可以很好地解决这个问题。我目前使用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
     ...

我想在我的threads 视图中呈现username,该视图按quality 排序。

我的服务:

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

由于某种原因,这会将看起来未完成的 observable 记录到控制台。

我想将这些值放入threads 的属性中,这样我就可以像这样在我的视图中呈现它:

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

更新:console.log 用于 allThreadsthisThread

更新:订阅 getUsername()

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

这样做的结果是没有值的对象:

【问题讨论】:

  • 您的getUsername 方法不返回任何内容。这是故意的吗?
  • 但是如果thisThread记录了那个对象,thisThread.username怎么可能是未定义的呢?
  • 嗯,你也可以仔细检查一下这条线吗? this.af.database.object('/members/' + memberKey + 'username')不应该是this.af.database.object('/members/' + memberKey + '/username')吗?
  • getUsername(memberKey: string) 那不是一个可观察的对象吗?你不需要订阅吗?就像你为 this.thread 所做的一样。
  • 提醒一下,如果您使用combineLatest,请删除first 运算符,以免内部可观察对象完成。

标签: angular typescript firebase firebase-realtime-database angularfire2


【解决方案1】:

您可以基于 getFeaturedThreads 编写一个 observable,它查询成员并将每个线程的 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 发射时发射的 observable。但是,如果用户名更改,则不会重新发出。如果这很重要,请将 forkJoin 替换为 combineLatest 并从组合成员 observables 中删除 first 运算符。

【讨论】:

  • 它们是 ES6 模板字面量:stackoverflow.com/questions/27565056/…
  • 我已经修复了括号/语法错误。你是说可观察的数组是空的吗?这应该只有在没有线程的情况下才有可能。
  • 看起来不错。之后会发生什么?错误?你的代码中有导入吗?你用的是什么版本的 RxJS?
  • 为了记录目的,在forkJoin 之后添加一个do 就像这样Observable.forkJoin(...memberObservables, () =&gt; threads).do(threads =&gt; console.log(threads)); 然后你应该看到发生了什么。每个thread 应该已经更新,作者和参与者被替换为用户名。所以模板应该使用{{thread.author}}等。参与者将是一个对象,所以你需要像你的earlier question那样枚举那些。
  • 记录成员 observable 中的值 do: .do(value =&gt; { console.log(value); thread.author = value.username; }) 并检查密钥是否存在等。并确保我在答案中使用的路径对于您的数据库结构是正确的等。答案的机制应该是合理的,它只是一些难以通过 cmets 调试的琐碎细节等。我想你快到了。
【解决方案2】:

为了解决用户连接问题,我编写了一个服务来缓存已经获取的用户,并用最少的代码将它们映射到引用数据中。它使用嵌套的映射结构来进行连接:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-25
    • 1970-01-01
    • 2018-02-19
    • 2023-03-17
    • 2017-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多