API Development

Data-binding Made Easy with Alloy: Part 1

With the Titanium Alloy MVC framework, developers can take advantage of built-in data-binding using Backbone.js, making binding data to views incredibly easy.

Traditionally in Titanium, you might bind some data to a TableView like this:

var tableView = Ti.UI.createTableView();
var rows = [];

myArrayOfData.forEach(function(item) {
    var row = Ti.UI.createTableViewRow();
    var thumb = Ti.UI.createImageView({
        left: 10,
        image: item.thumbnailUrl,
        width: 40,
        height: 40
    });
    var label = Ti.UI.createLabel({
        left: 60,
        text: item.title,
        width: Ti.UI.SIZE,
        height: Ti.UI.SIZE
    });
    row.add(thumb);
    row.add(label);
    rows.push(row);
});

tableView.setData(rows);

In this example, we’re creating an instance of a Ti.UI.TableView, then iterating an array of data (which we assume here contains objects with a thumbnailUrl and title property), creating a Ti.UI.TableViewRow, Ti.UI.Label, and Ti.UI.ImageView, and adding the image and label to the row.

Finally we push the row into a rows array and then after the loop, we’re applying the data to the TableView.

I know that a ListView is more performant and the preferred way to work with tabular data — I’ll cover the advantages and disadvantages of ListViews in another post. For the purposes of this post, we’re using a TableView example.

So what’s the issue with this approach?

Firstly, there’s a lot of code being written — and more code means more chance of making mistakes, having performance issues, memory leaks, not to mention larger files to navigate and debug.

Secondly, if we’re doing this in an Alloy project, we’re not taking advantage of XML layouts and TSS to style our elements, meaning we’re repeating code for position, style, fonts etc.

Of course you could use Alloy to move the TableViewRow into its own view, then use Alloy.createController within the loop to create an instance of that, do the binding of the values manually in the TableViewRow controller itself. This would be more Alloy-like and use TSS, XML etc. properly, but it’s still more files, more code, and possible issues down the line, especially if you’re working with multiple tables in your app.

Thankfully, Alloy comes with Backbone.js support which makes it really easy to bind data to TableViews, ListViews and other elements using minimal code and fitting nicely with the Alloy MVC approach.

The full-blown Alloy data-binding implementation uses Model and Collection configurations, so takes a little setup but you can simplify this by creating your own implementation of Backbone syncing.

To demonstrate this, I created a simple library called Mocx which is designed to override the default Backbone.js sync, allowing you to create Collections and Models on-the-fly and use them in your Alloy apps.

The library code is really simple:

Backbone.sync = function(method, model, options) {
    // overrides fetch() to trigger a bind via change()
    if (model instanceof Backbone.Collection) {
        console.warn("Collection sync: " + method);
    } else {
        console.warn("model sync: " + method);
    }
    model.trigger("change");
    options.success(model.toJSON());
};

exports.createModel = function(name, attributes) {
    var model = new Backbone.Model(attributes);
    return model;
};

exports.createCollection = function(name, content) {
    if (!Alloy.Collections[name]) {
        Alloy.Collections[name] = new Backbone.Collection();
    }

    if (content instanceof Array) {
        Alloy.Collections[name].reset(content);
    } else {
        throw "No Array specified for createCollection";
    }
};

That’s it — adding this to the /lib folder of an Alloy project and requiring it in your project means you can now create Alloy compatible Models and Collections on-the-fly from Arrays or JSON objects — you can even save and load them using Ti.App.Properties.

Taking our original example, and assuming we’ve added the mocx.js file to our app/lib folder — let’s update alloy.js to add the library and a mock collection called “posts”:

Alloy.Globals.mocx = require("mocx");

Alloy.Globals.mocx.createCollection("posts", [{
    thumbailUrl: "https://someimage",
    title: "First post"
}, {
    thumbailUrl: "https://someotherimage",
    title: "Second post"
}]);

Now, in our Index.xml view, we can have a simple template for our TableView:

<TableView dataCollection="posts">
  <TableViewRow>
    <ImageView class="thumbnail" image="{thumbnailUrl}"/>
    <Label class="title" text="{title}"/>
  </TableViewRow>
</TableView>

and in index.tss setup the styling:

".thumbnail" : {
  width: 30,
  height: 30,
  left: 10
}
".title" : {
  width: Ti.UI.SIZE,
  height: Ti.UI.SIZE,
  left: 60
}

Finally, in index.js, we add the following:

Alloy.Collections.posts.fetch();

The first thing to note here is that we’ve only written just one line of JavaScript to do the same data binding we did in the first example!

Since our view is now in XML, and our styles are in TSS, we don’t have to write much JavaScript at all to bind the data. What’s more, if we make any changes to the models in the collection that’s bound to the TableView, then the data will be automatically updated.

So for example, doing the following:

Alloy.Collections.posts.at(0).set(“title”, “title changed!”);

will change the value of the title property in the model AND the row will update in the TableView automatically.

In future posts I’ll be covering more advanced features of data-binding, including transformations, and how to render out model data easily to a detail view.

For more guides, check out Alloy Data Binding and the backbone.js documentation, and please leave a comment!