AngularFire2 无限滚动 [英] AngularFire2 infinite scrolling

查看:26
本文介绍了AngularFire2 无限滚动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Ionic2 和 Firebase 实现无限滚动.

我使用 AngularFire2.我想要做的是将新项目添加到获取的列表中,而不是重新加载整个列表.

let query$:Observable而且我还注意到每个下一个块都会发出 2 次请求(尽管我放置了 publishReplay).它发生在我使用 AngularFire2 的所有应用程序中.不过我可能会误解一些东西.我肯定需要澄清一下.

//==========编辑============

现在,我以某种方式设法实现了我想要的,而无需每次都重新加载整个列表.不是最好的实现,但它有效.基本上,我创建了一个 observable 数组,并通过订阅下一个块 observable(我也从最后一个元素开始)将新值加载到其中.然而,后来的问题仍然存在 - 在套接字显示中,我收到了 2 次请求的数据.

解决方案

将 observable 用于 query 选项就行不通了.底层 SDK 中没有工具来动态修改查询的 limitToFirst,AngularFire2 中也没有办法做到这一点.

每次可观察的 query 选项发出一个新值时,都会创建一个新的 Firebase 引用.你可以在 来源在这里.

但是,可以通过执行以下操作来创建一个表示无限列表的 observable:

import { Observable } from "rxjs/Observable";从rxjs/主题"导入{主题};导入 rxjs/add/observable/defer";导入 rxjs/add/observable/zip";导入 rxjs/add/operator/concatMap";导入 rxjs/add/operator/filter";导入 rxjs/add/operator/first";导入 rxjs/add/operator/map";导入 rxjs/add/operator/scan";导入 rxjs/add/operator/share";导入 rxjs/add/operator/startWith";const pageSize = 100;let notifier = new Subject();让最后:Observable;让infiniteList = Observable//使用 zip 将通知程序的排放与最后的排放结合起来//子值:.zip(notifier, Observable.defer(() => last))//使用 concatMap 将一页子元素发送到//组合 observable(注意,first 用于完成//内部列表):.concatMap(([unused, last]) => this.af.database.list("quests", {询问: {//如果有最后一个值,则从该值开始但询问//再来一个:limitToFirst:最后?(pageSize + 1) : pageSize,orderByChild: "date_published",开始于:最后}}).第一的())//使用scan将页面累积到无限列表中:.scan((acc, list) => {//如果这不是初始页面,则该页面已启动//在最后一个值,所以从开头删除它//列表:如果 (acc.length > 0) {list.shift();}返回 acc.concat(list);}, [])//使用 share 以便最后一个 observable(见下文)不会//导致第二次订阅:.分享();//每次发出页面时,映射到它的最后一个子值//它可以反馈到组合无限列表中:最后 = 无穷列表.filter((list) => list.length > 0).map((list) => list[list.length - 1].date_published).startWith(null);infiniteList.subscribe((list) => console.log(list));//每次通知程序发出时,将检索另一个页面//并添加到无限列表中:通知程序.next();通知程序.next();通知程序.next();

这会起作用,但是如果您订购的子级具有重复的值,AngularFire2 将无法可靠地翻阅结果,直到 这个问题 重新打开并解决.

结果列表是静态的.也就是说,如果数据库更改,已经分页到列表中的子项将不会更新.实现动态列表更具挑战性,因为基于限制的分页机制很容易影响重复和丢失的子项.

<小时>

自从写下这个答案以来,我已经在我开源的 Firebase observables 库中提供了前向和反向、非实时和实时无限列表可观测值的测试实现.请参阅此 GitHub 存储库.

I'm trying to implement an infinite scrolling with Ionic2 and Firebase.

I use AngularFire2. What I'd like to do is to add new items to the fetched list and not to reload the whole list.

let query$:Observable<any> = this.af.database.list(`quests/`, {
    query: {
        orderByChild: 'date_published',
        limitToFirst: this.recentChunkLimit$ //Subject where I push new limit length
    }
}).publishReplay(1).refCount();

However, when I query list like that, the whole list is reloaded each time via websockets making each next update slower and slower. Here is a screenshot of Network websockets tab: And also I noticed that requests are made 2 times for each next chunk (though I put publishReplay). And it is happening in all apps where I used AngularFire2. I might misunderstand something though. I definitely need some clarification.

//==========Edit============

Now, I somehow managed to implement what I want without reloading the whole list each time. Not the best implementation but it works. Basically, I created an observable array and load new values into it by subscribing to the next chunk observable (where I also get the last element to start with). However the later problem still remains - in the sockets display I get data requested 2 times.

解决方案

Using observables for query options just does not work like that. There is no facility in the underlying SDK to dynamically modify a query's limitToFirst and there's no way of doing it in AngularFire2.

Each time an observable query option emits a new value, a new Firebase ref is created. You can see it in the source here.

However, it would be possible to create an observable that represents an infinite list by doing something like this:

import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import rxjs/add/observable/defer";
import rxjs/add/observable/zip";
import rxjs/add/operator/concatMap";
import rxjs/add/operator/filter";
import rxjs/add/operator/first";
import rxjs/add/operator/map";
import rxjs/add/operator/scan";
import rxjs/add/operator/share";
import rxjs/add/operator/startWith";

const pageSize = 100;
let notifier = new Subject<any>();
let last: Observable<any>;

let infiniteList = Observable

  // Use zip to combine the notifier's emissions with the last
  // child value:

  .zip(notifier, Observable.defer(() => last))

  // Use concatMap to emit a page of children into the
  // composed observable (note that first is used to complete
  // the inner list):

  .concatMap(([unused, last]) => this.af.database.list("quests", {
      query: {

        // If there is a last value, start at that value but ask
        // for one more:

        limitToFirst: last ? (pageSize + 1) : pageSize,
        orderByChild: "date_published",
        startAt: last
      }
    })
    .first()
  )

  // Use scan to accumulate the page into the infinite list:

  .scan((acc, list) => {

    // If this isn't the initial page, the page was started
    // at the last value, so remove it from the beginning of
    // the list:

    if (acc.length > 0) {
      list.shift();
    }
    return acc.concat(list);
  }, [])

  // Use share so that the last observable (see below) doesn't
  // result in a second subscription:

  .share();

// Each time a page is emitted, map to its last child value so
// that it can be fed back into the composed infinite list:

last = infiniteList
  .filter((list) => list.length > 0)
  .map((list) => list[list.length - 1].date_published)
  .startWith(null);

infiniteList.subscribe((list) => console.log(list));

// Each time the notifier emits, another page will be retrieved
// and added to the infinite list:

notifier.next();
notifier.next();
notifier.next();

That will work, but if the child upon which you are ordering has duplicate values, AngularFire2 won't be able to page through the results reliably until this issue is re-opened and resolved.

The resultant list is static. That is, children already paged in to the list won't be updated if the database changes. Implementing a dynamic list is more challenging, as duplicated and missing children can easily be effected by the limit-based paging mechanism.


Since writing this answer, I've made available tested implementations of forward and reverse, non-realtime and realtime infinite list observables in a library of Firebase observables that I have open sourced. See this GitHub repo.

这篇关于AngularFire2 无限滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆