Skip to content

Commit

Permalink
working on es6 import
Browse files Browse the repository at this point in the history
  • Loading branch information
brandon flowers committed Mar 2, 2018
1 parent a34ebb5 commit 91acef3
Show file tree
Hide file tree
Showing 10 changed files with 1,046 additions and 2,431 deletions.
Binary file added .DS_Store
Binary file not shown.
77 changes: 38 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,33 @@ In summary: Node.js requires Promise and Axios; the browser requires Promise and

### Install

- NPM: ```npm install --save firebase-paginator```
- Bower: ```bower install --save firebase-paginator```
* NPM: `npm install --save firebase-paginator`
* Bower: `bower install --save firebase-paginator`

### Test

- ```npm install```
- ```npm test```
Find env.json.dist and copy it then rename the clone to env.json. This file is on the git ignore list. You will need to update this line: `"databaseURL": "https://your-firebase-database-name.firebaseio.com/"`

### Usage
You need to create a [firebase-admin](https://firebase.google.com/docs/admin/setup) account to get the required service-account.json file is also on the git ignore list.

If you're in Node.js, you'll need to do something like ```var FirebasePaginator = require('firebase-paginator')```.
* `npm install`
* `npm test`

If you're in the browser, you'll have access to FirebasePaginator on the ```window``` object like so: ```var FirebasePaginator = window.FirebasePaginator;```
### Usage

Once you have your ```FirebasePaginator``` object, the rest is isomorphic JavaScript. Just pass in a Firebase ref and some options:
If you're in Node.js, you'll need to do something like `var FirebasePaginator = require('firebase-paginator')`.

***pageSize***: any integer greater than zero, defaults to 10
If you're in the browser, you'll have access to FirebasePaginator on the `window` object like so: `var FirebasePaginator = window.FirebasePaginator;`

***finite***: defaults to false
Once you have your `FirebasePaginator` object, the rest is isomorphic JavaScript. Just pass in a Firebase ref and some options:

***auth***: optional auth token for secure collections
**_pageSize_**: any integer greater than zero, defaults to 10

***retainLastPage***: applies to infinite pagination only; prevents a short last page from resetting the list; see [Finite vs Infinite Pagination](#finite-vs-infinite-pagination)
**_finite_**: defaults to false

**_auth_**: optional auth token for secure collections

**_retainLastPage_**: applies to infinite pagination only; prevents a short last page from resetting the list; see [Finite vs Infinite Pagination](#finite-vs-infinite-pagination)

```
var options = {
Expand All @@ -46,8 +48,8 @@ var options = {
retainLastPage: false
};
var paginator = new FirebasePaginator(ref, options);
```

# Functions

#### FirebasePaginator.prototype.listen(callback)
Expand All @@ -63,8 +65,7 @@ var itemsList = [];
paginator.listen(function (eventName, eventPayload) {
console(`Fired ${eventName} with the following payload: `, eventPayload);
});
```

```

#### FirebasePaginator.prototype.on(event, callback)

Expand All @@ -78,7 +79,7 @@ var handler = function() {
};
paginator.on('value', handler);
```
```

#### FirebasePaginator.prototype.off(event, callback)

Expand All @@ -92,7 +93,7 @@ var handler = function() {
};
paginator.off('value', handler);
```
```

#### FirebasePaginator.prototype.once(event, callback) -> returns promise

Expand All @@ -110,23 +111,23 @@ paginator.once('value', handler);
// Promise pattern
paginator.once('value').then(handler);
```
```

#### FirebasePaginator.prototype.reset() -> returns promise

Resets pagination

Infinite: jumps to end of collection

Finite: Refreshes keys list and jumps to page 1
Finite: Refreshes keys list and jumps to page 1

```
var paginator = new FirebasePaginator(ref);
paginator.reset()
.then(function() {
console.log('list has been reset');
});
```
```

#### FirebasePaginator.prototype.previous() -> returns promise

Expand All @@ -138,8 +139,7 @@ paginator.previous()
.then(function() {
console.log('paginated backward');
});
```

```

#### FirebasePaginator.prototype.next() -> returns promise

Expand All @@ -151,23 +151,23 @@ paginator.next()
.then(function() {
console.log('paginated forward');
});
```
```

#### FirebasePaginator.prototype.goToPage(<page number>) -> returns promise

Jumps to any page

Accepts page numbers from 1 to the pageCount

Available for finite pagination ***only***
Available for finite pagination **_only_**

```
var paginator = new FirebasePaginator(ref);
paginator.goToPage(3)
.then(function() {
console.log('paginated to page 3');
});
```
```

# Events

Expand All @@ -187,45 +187,44 @@ FirebasePaginator fires its **ready** event once the first page is loaded.

The **reset**, **next** and **previous** events fire after each of the corresponding functions is complete and the new data is loaded.


# Finite vs Infinite Pagination

There are two ways to paginate Firebase data: finite and infinite paginations.

Let's assume that pageSize is 10 and we have records 1 through 100. Also note that all Firebase pagination occurs from the bottom of the collection.
Let's assume that pageSize is 10 and we have records 1 through 100. Also note that all Firebase pagination occurs from the bottom of the collection.

#### Infinite Pagination

Infinite pagination pulls the last 11 records of the collection, saves the 90th record's key as a cursor and adds records 91 through 100 to the collection.

Infinite pagination steps backward by pulling another 11 records ending at the cursor (a.k.a. the 90th record's key). So paging back once will display records 81 to 90 with record 80's key as the new cursor. Page back again and you're at records 71 to 80 and so forth.

By default, inifinite pagination resets its last page if you overrun the beginning of a list. For example, if you had 100 items and a ```pageSize``` of 30, paging backwards would return records 71-100, 41-70, 11-40 and 1-30. Notice that the last page is still 30 records. The default behavior is to reset the collection to the beginning of the list and return a full page if possible. The set ```retainLastPage: true``` in your options to return records 1-10 instead.
By default, inifinite pagination resets its last page if you overrun the beginning of a list. For example, if you had 100 items and a `pageSize` of 30, paging backwards would return records 71-100, 41-70, 11-40 and 1-30. Notice that the last page is still 30 records. The default behavior is to reset the collection to the beginning of the list and return a full page if possible. The set `retainLastPage: true` in your options to return records 1-10 instead.

Pros:

- Scales forever
- Users can page forward to discover new records as they're added to the collection
- If a user is on the first page, new records will simply appear as they are added
* Scales forever
* Users can page forward to discover new records as they're added to the collection
* If a user is on the first page, new records will simply appear as they are added

Cons:

- Must page forward and backward sequentially. Can't skip pages.
- No context for how many pages exist and where the user is in the list
- If a user is on the first page, new records will simply appear as they are added
* Must page forward and backward sequentially. Can't skip pages.
* No context for how many pages exist and where the user is in the list
* If a user is on the first page, new records will simply appear as they are added

#### Finite Pagination

Finite pagination makes a single "shallow" REST query to pull all of the collection's keys. See [the docs](https://firebase.google.com/docs/reference/rest/database/#section-param-shallow) on how this is done.
Finite pagination makes a single "shallow" REST query to pull all of the collection's keys. See [the docs](https://firebase.google.com/docs/reference/rest/database/#section-param-shallow) on how this is done.

Once FirebasePaginator has all of the keys, it sorts them and finds the page endpoints. So if we have 100 records with a pageSize of 10, the page endpoints will be the keys for records 10, 20, 30, 40, 50... 100.

Pros:

- Users have context for where they are in the collection.
- Users can skip pages.
* Users have context for where they are in the collection.
* Users can skip pages.

Cons:

- Beware of scaling issues. Consider archiving records to [Google Cloud Datastore](https://cloud.google.com/datastore/docs/) if the collection grows too large.
- Must call ```paginator.reset()``` to capture new records the may be added
* Beware of scaling issues. Consider archiving records to [Google Cloud Datastore](https://cloud.google.com/datastore/docs/) if the collection grows too large.
* Must call `paginator.reset()` to capture new records the may be added
121 changes: 121 additions & 0 deletions es6/FirebasePaginator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* eslint-disable */

import FinitePagingStrategy from './FirebasePaginatorFiniteStrategy';
import InfinitePagingStrategy from './FirebasePaginatorInfiniteStrategy';

class FirebasePaginator {
constructor(ref, defaults) {
this.defaults = defaults || {};
this.pages = {};
this.pageSize = defaults.pageSize ? parseInt(defaults.pageSize, 10) : 10;
this.isFinite = defaults.finite ? defaults.finite : false;
this.retainLastPage = defaults.retainLastPage || false;
this.auth = defaults.auth;
this.ref = ref;
this.isBrowser = defaults.isBrowser;
this.events = {};
this.pageCount;

// Events
this.listen = callback => {
this.allEventHandler = callback;
};

this.fire = this.fire.bind(this);
this.on = this.on.bind(this);
this.off = this.off.bind(this);
this.once = this.once.bind(this);

// Pagination can be finite or infinite. Infinite pagination is the default.
const paginator = this;
if (this.isFinite) {
//this.setupFinite();
this.strategy = new FinitePagingStrategy(paginator);
} else {
this.strategy = new InfinitePagingStrategy(paginator);
}

this.next = this.next.bind(this);
this.previous = this.previous.bind(this);
this.goToPage = this.goToPage.bind(this);

console.log('FirebasePaginator constructor this: ', this);
}

fire(eventName, payload) {
if (typeof this.allEventHandler === 'function') {
this.allEventHandler.call(this, eventName, payload);
}

if (this.events[eventName] && this.events[eventName].queue) {
const queue = events[eventName].queue.reverse();
let i = queue.length;
while (i--) {
if (typeof queue[i] === 'function') {
queue[i].call(this, payload);
}
}
}
}

on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = {
queue: []
};
}
this.events[eventName].queue.push(callback);
}

off(eventName, callback) {
if (this.events[eventName] && this.events[eventName].queue) {
const queue = this.events[eventName].queue;
let i = queue.length;
while (i--) {
if (queue[i] === callback) {
queue.splice(i, 1);
}
}
}
}

once(eventName, callback) {
return new Promise((resolve, reject) => {
const handler = payload => {
this.off(eventName, handler);
if (typeof callback === 'function') {
try {
resolve(callback.call(this, payload));
} catch (e) {
reject(e);
}
} else {
resolve(payload);
}
};
this.on(eventName, handler);
});
}

// strategies based on finite or infinite
next() {
return this.strategy.next();
}
previous() {
return this.strategy.previous();
}
reset() {
return this.strategy.reset();
}
goToPage(pageNumber) {
console.log('access this.strategy', this.strategy);

if (isFinite) return this.strategy.goToPage(pageNumber);
else
return new Promise((resolve, reject) => {
reject({ message: 'infinite does not support paging' });
});
}
}

export default FirebasePaginator;
Loading

0 comments on commit 91acef3

Please sign in to comment.