You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular+Firestore联系人数据获取问题及RxJS操作符困惑

问题:Angular结合Firestore获取联系人数据报错及解决方案

我对Angular的map类操作符理解不足,观看数小时视频教程仍未解决问题。场景为使用Firestore存储用户数据,每个用户拥有一个存储其他用户ID的数组,需要获取每个ID对应的用户数据并存入contacts数组。尝试通过contactData$ Observable基于当前用户数据创建流,但出现报错。

原代码

getFbContactData方法(构造函数中加载)

getFbContactData() {
  this.contacts = [];
  this.activeUsersDoc.get().pipe(switchMap(userSnapshot => {

      return from<string[]>(userSnapshot.data()['contacts']);
  })).pipe(concatMap((contactId) => {
         this.contactUsersDoc = this.usersCollection.doc(contactId);
         this.getContactData(contactId);
    return this.contactData$
  })).pipe(switchMap(() => this.contactData$)
  ).subscribe((contact) => {
  })
}

getContactData方法

getContactData(userId) {
  const contactUserDoc = this.usersCollection.doc(userId);
  contactUserDoc.get().pipe(map(userSnapshot => {

    return userSnapshot

  })).subscribe((userSnapshot) => {
    const contact: Contact = new Contact(
      {
        uid: userSnapshot.data()['uid'],
        firstName: userSnapshot.data()['firstName'],
        lastName: userSnapshot.data()['lastName'],
        initials: userSnapshot.data()['initials'],
        email: userSnapshot.data()['email'],
        displayName: userSnapshot.data()['displayName'],
      }
    )
    console.log(contact);

    this.contactData$ = of(contact);
  })
}

报错信息

core.mjs:8400 ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.
    at createInvalidObservableTypeError (throwUnobservableError.js:2:12)
    at innerFrom (innerFrom.js:37:43)
    at doInnerSub (mergeInternals.js:19:18)
    at outerNext (mergeInternals.js:14:57)
    at OperatorSubscriber._next (OperatorSubscriber.js:13:21)
    at OperatorSubscriber.next (Subscriber.js:31:18)
    at subscribe.innerSubscriber (switchMap.js:14:144)
    at OperatorSubscriber._next (OperatorSubscriber.js:13:21)
    at OperatorSubscriber.next (Subscriber.js:31:18)
    at Observable._subscribe (innerFrom.js:51:24)

解决方案

报错核心原因

  1. contactData$在concatMap中返回时处于undefined状态:getContactData是异步操作,调用后立刻返回contactData$,此时它还未被赋值。
  2. 手动订阅getContactData内的Observable破坏了流的链式结构,导致异步时序混乱。

重构后的代码

主方法getFbContactData

getFbContactData() {
  this.activeUsersDoc.get().pipe(
    // 获取当前用户的联系人ID数组
    switchMap(userSnapshot => {
      const contactIds = userSnapshot.data()?.['contacts'] as string[];
      // 处理无联系人的边界情况
      if (!contactIds || contactIds.length === 0) {
        return of([]);
      }
      // 将数组转为Observable流,逐个处理ID
      return from(contactIds);
    }),
    // 按顺序逐个获取联系人数据(保持顺序)
    concatMap(contactId => this.getContactData(contactId)),
    // 将所有联系人数据收集为一个数组
    toArray()
  ).subscribe({
    next: (contacts) => {
      this.contacts = contacts;
      console.log('已获取联系人列表:', contacts);
    },
    error: (err) => {
      console.error('获取联系人失败:', err);
    }
  });
}

联系人数据获取方法getContactData

// 改为返回Observable<Contact>,不再手动订阅
getContactData(userId: string): Observable<Contact> {
  const contactUserDoc = this.usersCollection.doc(userId);
  return contactUserDoc.get().pipe(
    map(userSnapshot => {
      const userData = userSnapshot.data();
      // 处理用户不存在的情况
      if (!userData) {
        throw new Error(`ID为${userId}的用户不存在`);
      }
      return new Contact({
        uid: userData['uid'],
        firstName: userData['firstName'],
        lastName: userData['lastName'],
        initials: userData['initials'],
        email: userData['email'],
        displayName: userData['displayName'],
      });
    })
  );
}

优化说明

  • 移除手动订阅:getContactData返回Observable,保持流的链式调用,避免异步时序问题。
  • 边界情况处理:增加了无联系人、用户数据为空的错误处理,提升代码健壮性。
  • 并行获取优化:如果不需要保持联系人顺序,可将from(contactIds)+concatMap替换为forkJoin实现并行获取,效率更高:
    switchMap(userSnapshot => {
      const contactIds = userSnapshot.data()?.['contacts'] as string[];
      if (!contactIds || contactIds.length === 0) {
        return of([]);
      }
      // 并行获取所有联系人数据
      const contactObservables = contactIds.map(id => this.getContactData(id));
      return forkJoin(contactObservables);
    })
    

内容的提问来源于stack exchange,提问作者IngoHe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 14:28:24