AngularFire2 infinite scrolling

I am trying to implement infinite scrolling using Ionic2 and Firebase.

I am using AngularFire2. What I would like to do is add new items to the selected list and not reload the entire 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 request a list like this, the whole list is reloaded every time through websites, making each next update slower and slower. Here is a screenshot of the Network websockets bookmark: websockets And also I noticed that requests are executed 2 times for each subsequent fragment (although I put publishReplay). And this happens in all applications where I used AngularFire2. I could understand something. I definitely need clarification.

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

Now I somehow managed to implement what I want without reloading the entire list every time. Not the best implementation, but it works. Basically, I created an observable array and loaded new values ​​into it, subscribing to the next observable block (where I also get the last element to start with). However, a later problem still remains - on the socket display I get the data requested 2 times. enter image description here

+2
angular infinite-scroll firebase ionic2 angularfire2
source share
2 answers

Using observable query parameters just doesn't work. There is no way to dynamically change the limitToFirst request in the base SDK, and no way to do this in AngularFire2.

Each time the observed query parameter generates 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 representing 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 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(); 

This will work, but if the child you are ordering for has duplicate values, AngularFire2 will not be able to reliably view the results until this question is opened and resolved again.

The resulting list is static. That is, children already on the list will not be updated if the database changes. Implementing a dynamic list is more complex since duplicate and absent children can be easily implemented using the swap mechanism based on restrictions.


After writing this answer, I made available a proven implementation of advanced and reverse, unrealistic and endless real-time observable lists in the Firebase observable object library that I have open. See this GitHub repository .

+5
source share

To add to the picker response , if you want to start from the end and get the list items in the reverse order, here is how you do it (I added comments where the code was changed).

 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(); 
+2
source share

All Articles