2017-02-07 11 views
2

Ionic2とFirebaseで無限スクロールを実装しようとしています。AngularFire2無限スクロール

私はAngularFire2を使用します。私がしたいのは、取り出したリストに新しいアイテムを追加し、リスト全体をリロードしないことです。私はそのようなリストを照会するとき

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(); 

しかし、リスト全体が遅く、遅く、それぞれ次の更新を行うことWebSocketを経由するたびにリロードされます。 ここにNetwork websocketsタブのスクリーンショットがあります: websockets また、次のチャンクごとにリクエストが2回発行されていることに気付きました(ただし、私はpublishReplayを入れましたが)。そして、私がAngularFire2を使ったすべてのアプリで起こっています。 しかし私は何かを誤解するかもしれません。私は明確な説明が必要です。

// ==========編集============

今、私は何とかリスト全体を毎回リロードせずに、私が欲しいものを実装するために管理します。最高の実装ではなく動作します。基本的には、観測可能な配列を作成し、次のチャンク観測可能性に登録することで新しい値をロードします(ここでは、最後の要素も取得します)。 しかし、後の問題は依然として残っています。ソケットディスプレイでは、2回データが要求されます。 enter image description here

+0

リスト全体の意味は?あなたは5アイテムをリクエストしました。その後、あなたは10を要求しました。最初の5つはあなたが要求した10の一部になります。それはあなたの期待ではありませんか? – Rexford

+0

私は、リスト全体がfirebaseから要求されているので、既に読み込まれたアイテムを再度読み込むことを意味しました。これは期待された動作ですが、それぞれの上限について望ましい動作ではなく、ローディング時間が長くなります。 –

答えて

5

オブザーバブルをqueryに使用すると、そのようには機能しません。基になるSDKにクエリのlimitToFirstを動的に変更する機能はなく、AngularFire2で行う方法はありません。

観測可能なqueryオプションが新しい値を放出するたびに、新しいFirebase refが作成されます。 source hereでご覧になれます。

しかし、このような何か実行して無限リストを表している観察可能な作成することが可能である:動作します

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(); 

をしていますが、注文された時に子供が重複する値を持っている場合、AngularFire2 this issueが再度開かれて解決されるまで、結果を確実にページングすることはできません。

結果リストは静的です。つまり、リストにページングされた子は、データベースが変更されても更新されません。動的なリストの実装は、重複していて欠落している子どもが制限ベースのページング・メカニズムによって簡単に影響を受けるため、より困難です。


は、この答えを書いているので、私はオープンソースを持っているFirebaseの観測のライブラリに順方向および逆方向の利用可能テスト実装、非リアルタイムとリアルタイム無限リストの観測を行いました。 this GitHub repoを参照してください。

+0

答えをありがとう。私はちょっと違った方法でそれを解決しました。あなたのソリューションはより美しいです。そして、それは良いRxJSレッスンです。また、質問の第2部分を見ることができますか?私はそれがこのようになっているのかどうかわかりません。 –

+0

2番目の部分は、情報が2回送信されるように見えることを意味しますか?私は明日それをすぐに見ていきます。 – cartant

+0

2回取得されたデータを示すWebSocketトラフィックに関しては、私がバグとみなすものが見つかりました。 AngularFire2チームと話しています。 – cartant

2

cartant's answerに追加する場合は、最後に開始し、逆順でリスト項目を取得する場合は、ここで行います(コードが変更されたときにコメントを追加しました)。

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 

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

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

     // Use limitToLast to move upward the list instead of downward 

     limitToLast: last ? (pageSize + 1) : pageSize, 
     orderByChild: "date_published", 

     // Use endAt to start at the end of the list 

     endAt: last 
     } 
    }) 
    .first() 
) 

    .scan((acc, list) => { 

    // Swap the roles of acc and list, as we want to 
    // concatenate from the beginning 

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

    .share(); 



last = infiniteList 
    .filter((list) => list.length > 0) 

    // Use the first child in this list as the next endAt value 

    .map((list) => list[0].date_published) 

    // Use undefined instead of null, as endAt: null in angularfire2 
    // will search for the last child that is null 

    .startWith(undefined); 

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

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