【问题标题】:Angular observable pipe return array角度可观察管道返回数组
【发布时间】:2021-01-06 05:46:40
【问题描述】:

我对 Angular 9 还很陌生,基本上我想知道如何从异步源返回对象数组?我目前有一个 firestore 查询,其中返回的文档有一个我希望呈现其数据的引用。

ngOnInit

this.currentUserRef.collection('links', ref => ref.where('pendingRequest', '==', false)).get()
            .pipe(map(querySnap => {
                const ret = [];
                querySnap.forEach(doc => {
                    const val = doc.get('otherUser').get().then(userData => {return userData});
                    ret.push(val);
                });
                return ret;
            })).subscribe(val => {
                this.links = val;
        });

HTML

<ion-item *ngFor="let link of links | async">
            <ion-avatar slot="start">
                <img [src]="getImage(link.get('profilepic'))">
            </ion-avatar>
            <ion-label>
                <h2>{{link.get('name')}}</h2>
                <p>{{link.get('bio')}}</p>
            </ion-label>
            <ion-checkbox id="{{link.id}}"></ion-checkbox>
        </ion-item>

这当前返回错误:InvalidPipeArgument: '[object Promise]' for pipe 'AsyncPipe' 当我删除 HTML 中的异步管道时,它会返回 zoneawarepromise。

编辑

this.links = this.currentUserRef.collection('links', ref => ref.where('pendingRequest', '==', false)).get()
            .pipe(map(querySnap => {
                const ret = [];
                querySnap.forEach(async doc => {
                    const val = await doc.get('otherUser').get().then(userData => {return userData});
                    ret.push(val);
                });
                return ret;
            }));

这现在可行,但是我不知道该解决方案对于大量文档的效率和可扩展性如何,因为 ret 数组 注意:querySnap 是一个对象,它有一个内置的 forEach 方法来循环遍历它的 docs 属性中的数组。

【问题讨论】:

    标签: angular google-cloud-firestore rxjs observable angular9


    【解决方案1】:

    通常不建议混合使用 Promise 和 observables。它们确实可以很好地配合使用,但它会混淆并混淆您的代码。将 promise 转换为 observable 也非常简单。大多数创建/处理高阶 observable 的操作符都会为您进行转换。否则,from(promise) 也会返回一个 observable。

    使用 forkJoin

    在这种情况下,forkJoin 接受 Promise 的数组而不会抱怨。

    forkJoin 将同时运行所有 Promise 并返回一个响应数组一次/如果它们都完成/解决。

    this.links = this.currentUserRef.collection(
      'links', 
      ref => ref.where('pendingRequest', '==', false)
    ).get().pipe(
      mergeMap(querySnap => forkJoin(
        querySnap.docs.map(
          doc => doc.get('otherUser').get()
        ))
      )
    );
    

    使用连接

    concat 也可以接受 Promise 作为参数。我们使用扩展运算符 (...) 将数组转换为参数列表。

    这实际上更接近于您通过 Promise 实现的目标,因为您在运行下一个 Promise 之前 await 每个 Promise。这将一次运行你的承诺(而不是同时运行,forkJoin 的方式

    this.links = this.currentUserRef.collection(
      'links', 
      ref => ref.where('pendingRequest', '==', false)
    ).get().pipe(
      mergeMap(querySnap => concat(
        ...querySnap.docs.map(
          doc => doc.get('otherUser').get()
        ))
      ),
      toArray()
    );
    

    旁白:清理你的承诺代码

    这个:

    this.links = this.currentUserRef.collection('links', ref => ref.where('pendingRequest', '==', false)).get()
                .pipe(map(querySnap => {
                    const ret = [];
                    querySnap.forEach(async doc => {
                        const val = await doc.get('otherUser').get().then(userData => {return userData});
                        ret.push(val);
                    });
                    return ret;
                }));
    

    等价于

    this.links = this.currentUserRef.collection(
      'links', 
      ref => ref.where('pendingRequest', '==', false)
    ).get().pipe(
      map(querySnap => {
        const ret = [];
        querySnap.forEach(async doc => 
          ret.push(await doc.get('otherUser').get())
        );
        return ret;
      })
    )
    

    基本上,如果你使用 await,你就不需要.then(...

    【讨论】:

    • 我不能用你的代码完全测试这个。你的错误是什么?
    • 所以 querySnap 是一个对象,其中包含一个名为 docs 的数组,我在 get('otherUser').get() 之间应用了 map 函数,我返回了包含我需要的数据的自定义对象渲染。它不仅没有渲染,而且我收到一条错误消息,说“您在预期流的位置提供了'未定义'。您可以提供 Observable、Promise、Array 或 Iterable。”当我尝试在 HTML 中呈现它时
    • 你的问题可能有问题?在您上面的编辑中,(您所说的有效)querySnap 是一个数组而不是一个对象。 - 此外,看起来 this.links 是未定义的。您是否正确使用了 Angular 的回调? (NgOnit 之类的?)。我不知道为什么这对你的上面有用
    • 抱歉,querySnap 有一个名为 forEach() 的内置方法,它遍历嵌入在对象中的数组。可以使用它,或者您可以提取对象中的 docs 数组并使用传统的数组循环方法。至于 Angular 的回调,我目前只使用 ngOninit,里面的代码是我在问题中提到的,你的修复补丁。
    • "可以使用,或者您可以提取对象中的 docs 数组并使用传统的数组循环方法。" - 试试看?我上面写的代码很适合我。我已将其更改为使用 querySnap.docs.map。看看有没有帮助?
    【解决方案2】:

    你可以试试这个:

                .pipe(map(querySnap => {
                    const ret = [];
                    querySnap.forEach(doc => {
                        const val = doc.get('otherUser').get().then(userData => {return userData});
                        ret.push(val);
                    });
                    return ret;
                })).map(val => {
                    this.links = of(val);
            });
    

    基本上你是新手,可以将可观察流添加到链接变量。

    【讨论】:

      猜你喜欢
      • 2021-11-14
      • 1970-01-01
      • 2019-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-27
      • 2020-09-02
      相关资源
      最近更新 更多