Firebase延迟加载rxjs

时间:2017-02-19 08:50:20

标签: firebase firebase-realtime-database rxjs reactive-programming angularfire2

我有以下代码,以获取最后一个chast消息:

const pageSize = 10;
this.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(this.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("chat_messages", {
      query: {

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

        limitToLast: last ? (pageSize + 1) : pageSize
      }
    })
    .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
  .map((list) => {
    list.reverse();
    if (list.length === 0) {
      return null;
    }
    return list[list.length - 1].date_published;
  })
  .startWith(null);

infiniteList.subscribe((list) => {
  this.chatMessages = list;
});
this.notifier.next();

每当用户滚动到列表底部时,我会this.notifier.next();从聊天记录中读取更多内容。一次发送10条消息。

问题:前10个效果很好,但当我滚动到底部读取新数据时,我会以相反的顺序获得相同的信息。例如,我有这个:

  

1-好

     

2-很好,你呢?

     

10-嗨!你好吗?

     

---滚动到上一条10条消息 -

     

12。大家好!你好吗?

     

13-。好的,你呢?

     

14-。好

1 个答案:

答案 0 :(得分:3)

以下实施应该可以正常工作。

请注意,组合的last observable使用filter在到达结尾(或开头,取决于您的观点)时停止发出值,最后一个键是页。

此外,页面需要反转 - 因为Firebase始终使用升序 - 并且反转应发生在合成的pages可观察对象中,并且页面需要在反转发生时复制。

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 pages = 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], index) => this.af.database
    .list("chat_messages", {
      query: {
        endAt: last,
        limitToLast: (index === 0) ? pageSize : (pageSize + 1)
      }
    })
    .first()
  )

  // 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 = pages
  .filter((page) => page.length > 0)
  .map((page) => page[0].$key)
  .startWith(undefined);

// Use scan to accumulate the pages into the infinite list. Copy
// the page - using slice - before reversing it:

let infiniteList = pages
  .scan((list, page) => {
    page = page.slice();
    page.reverse();
    if (list.length > 0) {
      page.shift();
    }
    return list.concat(page);
  }, []);

infiniteList.subscribe(observer);

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

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

这只能在使用orderByKey时可靠地运行。在按子级,值或优先级排序时,Firebase SDK的startAt方法支持可选的key参数。但是,endAt方法在按优先级排序时仅记录为支持key。这意味着使用endAtorderByChild进行分页将不可靠 - 如果子值不唯一且有多个项具有相同的子值,则无法始终进行分页。

关于无限列表的实时更新,这更复杂,因为基于限制的机制可以轻松地看到重复和丢失的项目受影响。但是,在列表的头部添加实时添加是相当简单的:

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

let pages = Observable
  .zip(notifier, Observable.defer(() => last))
  .concatMap(([unused, last], index) => this.af.database
    .list("chat_messages", {
      query: {
        endAt: last,
        limitToLast: (index === 0) ? pageSize : (pageSize + 1)
      }
    })
    .first()
  )
  .share();

last = pages
  .filter((page) => page.length > 0)
  .map((page) => page[0].$key)
  .startWith(undefined);

let tail = pages
  .scan((list, page) => {
    page = page.slice();
    page.reverse();
    if (list.length > 0) {
      page.shift();
    }
    return list.concat(page);
  }, []);

// When the first non-empty page is emitted, create an observable
// that watches items starting at the first item in the first page:

let head = pages
  .filter((page) => page.length > 0)
  .take(1)
  .switchMap((page) => this.af.database
    .list("chat_messages", {
      query: {
        startAt: page[page.length - 1].$key
      }
    })
  )
  .map((list) => list.slice(1));

// And combine that with the pages:

let infiniteList = Observable
  .combineLatest(
    head,
    tail,
    (head, tail) => head.concat(tail)
  );

infiniteList.subscribe(observer);

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