【Firebase】FireStoreとVueでページング処理【タイムライン】
前書き
実装の一つのテーマとしてTwitterのタイムラインのような機能が欲しいなと思って実装しました。
FireStoreから取得したデータと画像をタイムライン形式に表示し、
ボタンを押すと、次の投稿をロードできます。
【実装】auth.js(FireStoreとのアクセスを担うスクリプト)
export async function getAllData(limit, pagingToken) { let nextToken = ""; let query = db.collection("comments").orderBy('createdAt', 'desc').limit(limit); if (pagingToken != "") { const [seconds, nanoseconds] = pagingToken.split(':'); const timestamp = new firebase.firestore.Timestamp(seconds, nanoseconds); query = query.startAfter(timestamp); } const result = await query.get().then((snapshot) => { if (snapshot.docs.length >= limit) { const last = snapshot.docs[snapshot.docs.length - 1]; const lastData = last.data(); const time = lastData.createdAt; nextToken = `${time.seconds}:${time.nanoseconds}`; } return { "BuffData": snapshot, "nextPageToken": nextToken }; }).catch(() => { alert("エラーが発見されました:データ取得時"); }); return result; }
解説
まず投稿処理を記述したスクリプトにFireStoreに格納するデータに日付を保存するよう記述します。
取得時にクエリを発行し、投稿日時順に情報が呼び出されます。
ここでは投稿が新しい順に取得されます。最大3件まで取得します。
let query = db.collection("comments").orderBy('createdAt', 'desc').limit(limit);
ここでは取得した件数がlimit以上であれば、まだ投稿が残っている or 最後の投稿を取得し終えた事になります。
取得したLimit件の投稿の中で最も古い投稿の時間を取得し、nextTokenとして保存します。
これによりnextTokenを頼りに投稿を遡っていきます。
const result = await query.get().then((snapshot) => { if (snapshot.docs.length >= limit) { const last = snapshot.docs[snapshot.docs.length - 1]; const lastData = last.data(); const time = lastData.createdAt; nextToken = `${time.seconds}:${time.nanoseconds}`; } return { "BuffData": snapshot, "nextPageToken": nextToken }; }).catch(() => { alert("エラーが発見されました:データ取得時"); });
タイムラインにてロードボタンを押した際に、
前回の記事の投稿時間をnextTokenに取得したままですので、
これより前の投稿を取得するstartAfterクエリを追加します。
if (pagingToken != "") { const [seconds, nanoseconds] = pagingToken.split(':'); const timestamp = new firebase.firestore.Timestamp(seconds, nanoseconds); query = query.startAfter(timestamp); }
【実装】Home.vue
<v-btn wrap row justify-center v-if="pagingToken" @click="nextPaging();" color="primary">次のページ</v-btn>
ロードボタンは前投稿日時であるpageTokenが存在している(まだ投稿が残っている or ちょうど最後の投稿を取得し終えた)場合に出現します。
data() { return { pagingToken: "", maps_data: null }; }, methods: { nextPaging: async function() { let data = []; data = await getAllData(3, this.pagingToken); let buffData = await downloadImageToBox(data.BuffData); this.maps_data = this.maps_data.concat(buffData); this.pagingToken = data.nextPageToken; },
ロードボタンを押した際の挙動をmethodsに記載します。
getAllData関数とdownloadImageToBox関数(画像のダウンロード=>詳細は後日)はJSファイルからImportしています。
初期状態ページがマウントされた際はmountedにて記述された処理が実装されます。【ここでは最新3件の情報を取得】
最新3件の情報はmaps_dataオブジェクトに格納されます。
しかしmountedにて取得された情報より前の投稿を取得するにはロードボタンを押さなくてはなりません。
ロードボタンを押すとmethodsのnextPaging()が実行されます。
ここで取得した次の3件(=buffData)が元々タイムラインにて存在していた最新の3件(=maps_data)と結合し、
maps_dataは6件となります。よってmaps_dataはタイムラインにて表示される投稿が格納されるわけです。
watch: { maps_data: async function() {} },
maps_dataに変更(TLに表示されて欲しい投稿の変更)が確認された場合は、
すぐさまタイムラインへ反映されて欲しいので、指定したデータの変更を検知してくっるwatchプロパティを活用します。
結果
参考
あとがき
無限スクロールも試したいですね。