Axway Appcelerator Blog

The Leading Resource for All Things Mobile

Data-binding Made Easy with Alloy: Part 1

11 Flares 11 Flares ×

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: "http://someimage",
    title: "First post"
}, {
    thumbailUrl: "http://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!

11 Flares Twitter 0 Facebook 0 Google+ 0 LinkedIn 11 Email -- 11 Flares ×

8 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

  1. Don’t think you need the var in:
    var Alloy.Globals.mocx = …..
    ?

    • Jason Kneen

      Good spot — fixed.

  2. adriendillens

    It is a good thing to have more information on the functioning of the data binding (especially between instances and singletons).
    We have a real expectation with regard to the transformation functions on the model and the collections, because the problem is the following:
    – Why is the model (or collection) generated in the view not recoverable in the controller?
    We have some answers with this post (http://www.appcelerator.com/blog/2016/05/alloy-data-binding-revisited/).
    This forces us to “tinker” with alternative solutions.
    I’m looking forward to a post on a detailed view of a transformed model (an example of a friend profile).
    Thanks a lot 😉

    • Jason Kneen

      Thanks for the comment — I’ll be covering this in an upcoming post — essentailly, you can pass a model to a controller using the $model parameter so Alloy.createController(“name”, {$model: modelInstance}); and this will make the model available in the controller. This allows you to bind the model elements to a detail view, for example, using backbone and Alloy binding. No need to do it manually :)

  3. Chrystoffer Kugler Horochovec

    Very useful! Tks

    • Chrystoffer Kugler Horochovec

      A question..
      This -> Alloy.Collections.posts.at(0).set(“title”, “title changed!”) <- does it work on version 0.9.2 of the backbone?

      • Jason Kneen

        I’ve used this for a while (since at least 2015) with no issues so it should work fine.

        • Chrystoffer Kugler Horochovec

          It worked!! thank you so much

Sign up for our blog!

Become a mobile leader. Take the first step to scale mobile innovation throughout your enterprise.
Get in touch
computer and tablet showing Appcelerator software
Start free, grow from there.
11 Flares Twitter 0 Facebook 0 Google+ 0 LinkedIn 11 Email -- 11 Flares ×