2017-02-07 17 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(); 

Однако, когда я запрашиваю список вроде этого, весь список перезагружается каждый раз через WebSockets делая каждое следующее обновление медленнее и медленнее. Вот скриншот вкладки 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. Не могли бы вы также взглянуть на вторую часть вопроса. Я не знаю, должно ли это быть таким образом; –

+0

Во второй части, вы имеете в виду информацию, отправляемую дважды? Завтра я быстро посмотрю на это. – cartant

+0

Что касается трафика 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();