Alloy widget for infinitive scrolling TableViews and ListViews
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
controllers validating in state function Apr 4, 2017
views Update widget.xml Jan 24, 2014

Alloy Infinite Scroll widget

gitTio René Pot

I've taken this widget into my management. Many thanks to Fokke Zandbergen for creating and maintaining it for all this time.

The Infinite Scroll widget implements the design pattern also known as Dynamic Scroll or Endless Scroll for TableViews and ListViews in the Alloy MVC framework for Titanium by Appcelerator. A Titanium Classic implementation can be found in the KitchenSink.

Also take a look at my Pull to Refresh widget.


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.



  • 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.

Quick Start

Get it gitTio

Download this repository and consult the Alloy Documentation on how to install it, or simply use the gitTio CLI:

$ gittio install nl.fokkezb.infiniteScroll

Use it

  • Add the widget to your TableView:

     <TableView id="table">
       <Widget id="is" src="nl.fokkezb.infiniteScroll" onEnd="myLoader" />

    or ListView:

     <ListView id="list">
       <Widget id="is" src="nl.fokkezb.infiniteScroll" onEnd="myLoader" />
  • Since Alloy 1.3.0 you have to manually bind the parent from the controller:

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

     // NOTE: do not use: var myLoader = function(e) {
     function myLoader(e) {
     	var ln = myCollection.models.length;
     		// whatever your sync adapter needs to fetch the next page
     		data: { offset: ln },
     		// don't reset the collection, but add to it
     		add: true,
     		success: function (col) {
     			// call done() when we received last page - else success()
     			(col.models.length === ln) ? e.done() : e.success();
     		// call error() when fetch fails
     		error: function(col) {
     			// pass optional error message to display
     			e.error(L('isError', 'Tap to try again...'));

    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!


The widget can be fully styled without touching the widget source. For Alloy 1.4 and later use the classnames below in your theme's app/themes/your-theme/widgets/nl.fokkezb.infiniteScroll/styles/widget.tss. For Alloy 1.3 and earlier use the ID's in your app's app.tss to override the default style, which is set on the classes.

class/ID Description
is The view to be added as FooterView
isCenter Can be used to align the content, but mainly to support iOS7's Ti.UI.Window.extendEdges by setting bottom to the height of the TabGroup (49).
isIndicator The ActivityIndicator showing during load
isText The message shown when not loading. Set visible to false if you want to hide the text until the first load has happened.


There are no required options to pass via TSS properties or XML attributes, apart from the onEnd attribute to bind your callback to the end-event. You can change the displayed messages by using the following options:

Parameter Type Default
msgTap string Tap to load more...
msgDone string No more to load...
msgError string Tap to try again...


You can also manually trigger the loading state of the widget. You could use this for the first load when your window opens.

Function Parameters Usage
setOptions object Set any of the options
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.
detach Manually set the DONE state and remove the scroll listener
cleanup Detach and then unset the footerView
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 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.


There is a test app and instructions in the test branch.


  • 1.4.5:
    • Fixes error on empty lists, renames dettach() to detach() and adds cleanup().
  • 1.4.4:
  • 1.4.3:
    • Closes #30 so you can call .mark() to re-init the position tracking.
  • 1.4.2:
    • Fixes #29 for Alloy 1.5.0
  • 1.4.1:
    • Fixes support for ListViews (marker loop after calling e.done())
    • Anticipating future Alloy version where __parentSymbol works again
  • 1.4.0:
    • Adds support for ListViews.
  • 1.3.3:
    • Fixes #25 when using the widget with no XML.
  • 1.3.2:
    • Workaround for regression in Alloy 1.3.0-cr
  • 1.3.1:
    • Fixes scroll-load-state loop with fast syncs.
  • 1.3:
    • Compatible with iOS7's new Ti.UI.Window.extendEdges via #isCenter.
    • Allows you to hide the text until first load by calling show after that
  • 1.2:
    • Now compatible with Android (and other OS)
    • View will now always show since Android doesn't support removing it :(
  • 1.1:
    • From now on declared in the XML view instead of the controller!
    • Splitted init into setOptions and attach
    • Renamed remove to dettach
    • Renamed trigger to load to not interfere with BackBone
  • 1.0.1:
    • Fixed for Alloy 1.0GA
  • 1.0: Initial version itial version


Copyright 2013 Fokke Zandbergen

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
See the License for the specific language governing permissions and
limitations under the License.