Skip to content

Commit

Permalink
Add list view support
Browse files Browse the repository at this point in the history
  • Loading branch information
FokkeZB committed Jul 17, 2014
1 parent c0578eb commit 4d55d77
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 41 deletions.
41 changes: 26 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
# Alloy *Infinite Scroll* widget [![Titanium](http://www-static.appcelerator.com/badges/titanium-git-badge-sq.png)](http://www.appcelerator.com/titanium/) [![Alloy](http://www-static.appcelerator.com/badges/alloy-git-badge-sq.png)](http://www.appcelerator.com/alloy/)
The *Infinite Scroll* widget implements the design pattern also known as *Dynamic Scroll* or *Endless Scroll* for the [Alloy](http://docs.appcelerator.com/titanium/latest/#!/guide/Alloy_Quick_Start) MVC framework for [Titanium](http://www.appcelerator.com/platform) by [Appcelerator](http://www.appcelerator.com). A Titanium Classic implementation can be found in the [KitchenSink](https://github.com/appcelerator/KitchenSink/blob/master/Resources/ui/handheld/ios/baseui/table_view_dynamic_scroll.js).
# Alloy *Infinite Scroll* widget [![Alloy](http://www-static.appcelerator.com/badges/alloy-git-badge-sq.png)](http://www.appcelerator.com/alloy/)
The *Infinite Scroll* widget implements the design pattern also known as *Dynamic Scroll* or *Endless Scroll* for TableViews and ListViews in the [Alloy](http://docs.appcelerator.com/titanium/latest/#!/guide/Alloy_Quick_Start) MVC framework for [Titanium](http://www.appcelerator.com/platform) by [Appcelerator](http://www.appcelerator.com). A Titanium Classic implementation can be found in the [KitchenSink](https://github.com/appcelerator/KitchenSink/blob/master/Resources/ui/handheld/ios/baseui/table_view_dynamic_scroll.js).

Also take a look at my [Pull to Refresh](https://github.com/FokkeZB/nl.fokkezb.pullToRefresh) widget.

## Overview
The widget automatically shows an *ActivityIndicator* in a *TableView*'s *FooterView* when the user reached the end of the table. An event is triggered so that the implementing controller can load more rows.
The widget automatically shows an *ActivityIndicator* in a *TableView* or *ListView*'s *FooterView* when the user reached the end of the table. An event is triggered so that the implementing controller can load more rows.

![screenshot](https://raw.github.com/FokkeZB/nl.fokkezb.infiniteScroll/master/docs/screenshot.png)

## Features
* Add the widget to your *TableView* using just one line of code.
* Add the widget to your *TableView* or *ListView* using just one line of code.
* Override all styling via your app's `app.tss`.
* Manually trigger the widget from your controller.

## Future work
* Full Android, Mobile Web, Tizen and BlackBerry compatibility and testing.
* Support for *ListView*s.

## Quick Start

### Get it [![gitTio](http://gitt.io/badge.png)](http://gitt.io/component/nl.fokkezb.infiniteScroll)
Expand All @@ -33,41 +29,53 @@ Download this repository and consult the [Alloy Documentation](http://docs.appce
<Widget id="is" src="nl.fokkezb.infiniteScroll" onEnd="myLoader" />
</TableView>
```

or *ListView*:

```xml
<ListView id="list">
<Widget id="is" src="nl.fokkezb.infiniteScroll" onEnd="myLoader" />
</ListView>
```

* Since Alloy 1.3.0 you have to manually bind the table from the controller:
* Since Alloy 1.3.0 you have to manually bind the parent from the controller:

```
$.is.init($.table);
$.is.init($.list);
```

* In the callback set via `myLoader` you call either `e.success()`, `e.error()` or `e.done()` optionally passing a custom message.

```javascript
function myLoader(e) {

// Length before
var ln = myCollection.models.length;

myCollection.fetch({

// Some data for the sync adapter to retrieve next "page"
// whatever your sync adapter needs to fetch the next page
data: { offset: ln },

// Don't reset the collection, but add to it
// don't reset the collection, but add to it
add: true,

success: function (col) {
// Call "done" if we didn't add anymore models
// call done() when we received last page - else success()
(col.models.length === ln) ? e.done() : e.success();

},

// call error() when fetch fails
error: e.error
});
}
```

Please note that in the example above I use `col.models.length` instead of `col.length`. There is a flaw in Backbone that will cause unpredictable lengths when more then 1 sync is performed at the same time.

* To do the initial fetch call `$.is.load()`. If you add/remove items via other ways then the widget and you're using a *ListView* then call `$.is.mark()` after that!


## Styling
The widget can be fully styled without touching the widget source. Use the following ID's in your app's `app.tss` to override the default style:
Expand Down Expand Up @@ -97,9 +105,12 @@ You can also manually trigger the loading state of the widget. You could use thi
| load | | Manually trigger the `end` event and loading state
| state | `state`, `string` | Manually set the state. The first argument should be one of the exported `SUCCESS`, `DONE` and `ERROR` constants. The second optional argument is a custom message to display instead of the message belonging to the state.
| dettach | | Manually set the `DONE` state and remove the scroll listener
| init | `Ti.UI.TableView` | Manually init the widget if it's the child element of the table, or to work around [TC-3417](https://jira.appcelerator.org/browse/TC-3417) in Alloy 1.3.0-cr.
| init | `Ti.UI.TableView`, `Ti.UI.ListView` | Manually init the widget if it's the child element of the *TableView* or *ListView*, or to work around [TC-3417](https://jira.appcelerator.org/browse/TC-3417) in Alloy 1.3.0 and later.
| mark | | If add/remove items from the *ListView* via other ways then the widget call `mark()` so the widget is triggered on the last item.

## Changelog
* 1.4.0:
* Adds support for *ListViews*.
* 1.3.3:
* Fixes #25 when using the widget with no XML.
* 1.3.2:
Expand Down
53 changes: 31 additions & 22 deletions controllers/widget.js
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@ var options = {

var loading = false,
position = null,
list = false,
currentState = 1;

// Not in all Alloy versions (1.3.0-cr)
// Only before Alloy 1.3.0
if (__parentSymbol) {
init();
}

function init(_table) {
function init(parent) {

// Override __parentSymbol
if (_table) {
__parentSymbol = _table;
// Override __parentSymbol (needed after Alloy 1.3.0)
if (parent) {
__parentSymbol = parent;

// manually add the footerView
__parentSymbol.footerView = $.is;
}

list = (__parentSymbol.apiName && __parentSymbol.apiName !== 'Ti.UI.TableView');

// delete special args
delete args.__parentSymbol;
delete args.__itemTemplate;
Expand All @@ -38,22 +41,30 @@ function init(_table) {
$.isCenter.remove($.isIndicator);

// listen to scroll or marker
if (__parentSymbol.apiName && __parentSymbol.apiName !== 'Ti.UI.TableView') {
var sectionIndex = __parentSymbol.sectionCount - 1;
__parentSymbol.setMarker({
sectionIndex: sectionIndex,
itemIndex: __parentSymbol.sections[sectionIndex].items.length - 1
});
if (list) {
mark();
__parentSymbol.addEventListener('marker', load);
} else {
__parentSymbol.addEventListener('scroll', onScroll);
}

// load when clicking on view
$.is.addEventListener('click', load);

return;
}

function mark() {

// sectionCount can be 0 on Android?!
var sectionIndex = Math.max(0, __parentSymbol.sectionCount - 1);

__parentSymbol.setMarker({
sectionIndex: sectionIndex,
itemIndex: __parentSymbol.sections[sectionIndex].items.length - 1
});
}

function state(_state, _message) {

// remove indicator
Expand All @@ -74,16 +85,12 @@ function state(_state, _message) {
$.isCenter.add($.isText);
$.isText.show(); // so it can be hidden on init via TSS

if (__parentSymbol.apiName && __parentSymbol.apiName !== 'Ti.UI.TableView') {
var sectionIndex = __parentSymbol.sectionCount - 1;
__parentSymbol.setMarker({
sectionIndex: sectionIndex,
itemIndex: __parentSymbol.sections[sectionIndex].items.length - 1
});
if (list) {
mark();
}

// small time-out to prevent scroll-load-state loop with fast syncs
setTimeout(function () {
setTimeout(function() {
loading = false;
}, 25);

Expand Down Expand Up @@ -123,7 +130,8 @@ function load() {

function onScroll(e) {

if (e.source.apiName && e.source.apiName !== 'Ti.UI.TableView') {
// fixes responding to bubbled scroll event
if (e.source !== __parentSymbol) {
return;
}

Expand All @@ -148,7 +156,7 @@ function onScroll(e) {

// trigger
if (triggerLoad) {
load();
load();
}

return;
Expand All @@ -160,8 +168,8 @@ function dettach() {
state(exports.DONE);

// remove listener
if (__parentSymbol.apiName && __parentSymbol.apiName !== 'Ti.UI.TableView') {
__parentSymbol.removeEventListener('marker', onScroll);
if (list) {
__parentSymbol.removeEventListener('marker', load);
} else {
__parentSymbol.removeEventListener('scroll', onScroll);
}
Expand Down Expand Up @@ -204,3 +212,4 @@ exports.load = load;
exports.state = state;
exports.dettach = dettach;
exports.init = init;
exports.mark = mark;
8 changes: 4 additions & 4 deletions widget.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"id": "nl.fokkezb.infiniteScroll",
"name": "nl.fokkezb.infiniteScroll",
"description" : "Scroll down to the end of the TableView to load more.",
"name": "Infinite Scroll for TableView and ListView",
"description" : "Scroll down to the end of the TableView or ListView to load more.",
"author": "Fokke Zandbergen",
"version": "1.3.3",
"copyright":"Copyright (c) 2013",
"version": "1.4.0",
"copyright":"Copyright (c) 2014",
"license":"Apache License, Version 2.0",
"min-alloy-version": "1.0",
"min-titanium-version":"2.1",
Expand Down

0 comments on commit 4d55d77

Please sign in to comment.