Appcelerator Blog

The Leading Resource for All Things Mobile

Alloy Data Binding Revisited

12 Flares 12 Flares ×

When we started this week I asked you what you’d like to see a blog post about and you were loud and clear:

topic

Changes to Alloy data binding

Throughout Alloy 1.8 we’ve fixed a few data binding edge cases and added support for some new features and applications. Alloy 1.8.2 shipped with AppC CLI 5.2.1 and both the current 5.2.2 and upcoming 5.3.0 include Alloy 1.8.5.

For Alloy 1.9.0 we’ve even completely rewritten data binding. It introduces some new features and also fixes the current known issue that data binding will always return string values.

Getting Alloy 1.9.0

Alloy 1.9.0 has been available on NPM for a while already and will ship with 5.4 in June. However, if you AppC CLI or Studio and already would like to use Alloy 1.9.0 then have at look at Falko’s gist explaining how to do that.

BE AWARE: The above gist does not work with LiveView enabled. It will still use the AppC embedded Alloy version.

See Understanding the Unified CLI to learn the difference between the stand-alone Titanium and Alloy CLIs and the ones bundled with the new unified AppC CLI.

To find your Alloy version run [appc] alloy -v.

Let’s Recap

Before we dive into the new features, let’s recap on how it works. Assuming you are familiar with the Alloy Data Binding guide here’s some key definitions to make sense of the fixes and changes.

Recap: Collection vs Model data binding

You can bind both a collection of models or an individual model. To bind a model attribute the opening curly bracket is first followed by the model name and then the attribute. To bind a collection you add the dataCollection attribute to the container using the collection name as value. The generated code will then loop over the collection and add the child elements to the container for each model.

<Alloy>
	<Model src="currentCategory" />
	<Collection src="book" />
	<Window>
	
		<!-- model data binding -->
		<Label text="{currentCategory.name}" />
		
		<!-- collection data binding -->
		<ScrollView dataCollection="book" />
			<Label text="{title}" />
		</ScrollView>
		
	</Window>
</Alloy>
		

Recap: Global singleton vs Local instance

In the above code snippet the model and collection are global singletons under Alloy.Model.currentCategory and Alloy.Collection.book. You can also use local instances for the current controller by adding instance="true" as attribute. You also need to assign them an ID in order to reference them in the XML and controller.

<Alloy>
	<Model src="currentCategory" instance="true" id="c" />
	<Collection src="book" instance="true" id="b" />
	<Window>
	
		<!-- model data binding -->
		<Label text="{$.c.name}" />
		
		<!-- collection data binding -->
		<ScrollView dataCollection="$.b" />
			<Label text="{title}" />
		</ScrollView>
		
	</Window>
</Alloy>

Recap: Simple vs Complex data binding

It’s important to understand the difference between simple and complex data binding since until Alloy 1.9 they we’re implemented in unique ways, resulting in different behaviour.

Simple data binding involves one model attribute where complex data binding involves a combination of strings (including white space) and model attributes or even multiple model attributes:

<Alloy>
	<Model src="book">
	<Window>
	
		<!-- simple -->
		<Label text="{book.title}" />
		
		<!-- complex -->
		<Label text="Title: {book.title}" />
		<Label text="{book.author.name} {book.author.email}" />
		
	</Window>
</Alloy>

New: Bind deep object properties

Starting with Alloy 1.9 you can bind deep object properties:

<Alloy>
	<Model src="book" />
	<Label text="{book.author.name}" />
</Alloy>

Before, you needed to use a transformer to create a reference like authorName.

New: Use models & properties names with special characters

Starting with Alloy 1.9 you can bind models and properties that use names with special characters like dashes and spaces. Simply wrap the names in square brackets and quotes like you’d do in JavaScript:

<Alloy>
	<Model src="my-model">
	<Label text="['my-model']['my-property']" />
</Alloy>

New: Bind multiple models to the same view

Also new in Alloy 1.9 is the ability to bind multiple models to the same view:

<Alloy>
	<Model src="a" />
	<Model src="b" />
	<Label text="{a.hello} {b.world}" />
</Alloy>

New: Define transformations in the model

Until Alloy 1.8.1 only simple model data-binding would call an optional transform() method on the model to allow you to provide derived properties. It was broken for complex data binding and not implemented for collection data binding.

Since Alloy 1.8.1 all types of data binding will generate the following logic to determine what object will be bound to the view. Note that only with collection binding you can also define a controller-based transform function to use via the dataTransform XML attribute.

var t;
if (_.isFunction(<dataTransform>) { // only for collection binding
	t = <dataTransform>(model);
} else if (_.isFunction(model.transform) {
	t = model.transform();
} else {
	t = model.toJSON();
}
$.myLabel.text = t.author.name;

NOTE: This does mean that the transform method need to return all bound properties, not just the transformed ones. Until Alloy 1.8.1 simple collection data binding did not require this and automatically felt back to the model attributes.

You’d extend a model with a transform() method as such:

exports.definition = {

	// config
  
	extendModel: function(Model) {
		_.extend(Model.prototype, {
			transform: function() {
			
				// get model attributes as object
				var t = this.toJSON();
				
				// override/add transformed properties
				t.titleCaps = t.title.toUpperCase();

				return t;
			}
		});
		
		return Model;
	}
};

Trick: Lazy transformation

The advantage of defining transformations in the model is that you don’t need to repeat them in every controller where you use a collection of these models. And of course it already was the only way to transform attributes for model data binding.

A possible disadvantage however is that everywhere you bind the model all transformations are computed were you might only need some or even none at all.

You can handle this using Object.defineProperty(). It’s get callback will only be called when the transform key is actually requested. This can improve the performance of you app in particular if you have multiple heavy transformations, like formatting a date:

var moment = require('alloy/moment');

exports.definition = {

	// config
  
	extendModel: function(Model) {
		_.extend(Model.prototype, {
			transform: function() {
				var model = this;
				var t = this.toJSON();
				
				Object.defineProperty(t, 'dateFormatted', {
				  get: function() {
				    return moment(t.date).format('LLLL');
				  }
				});

				return t;
			}
		});
		
		return Model;
	}
};

Trick: Populating a model after data binding

When Alloy compiles your views and controllers, the generated view code precedes your controller code. Any models you define for data binding in the XML will also be created at that point. So how would you bind an already existing model? I’ve seen several workarounds for this, but if you think about it it is actually not that much different from binding a collection, which also starts off empty. Just like you call fetch() to populate the collection, you do the exact same thing for the model. Depending on the sync adapter you either pass the query/ID as options or first set the model’s id-attribute and then call fetch().

index.xml

<Alloy>
	<Model src="book" instance="true" id="current" />
	<Window>
		<Label text="{book.title}" />
	</Window>
</Alloy>

index.js

$.current.fetch({
	id: Ti.App.Properties.getString('currentBook')
});

$.index.open();

Tracker as Example

The Tracker App I recently started building requires Alloy 1.9 and demonstrates collection data binding, model data binding, fetching a collection from a query as well as lazy transformations and populating an existing model.

Code Strong! 🚀

12 Flares Twitter 0 Facebook 0 Google+ 3 LinkedIn 9 Email -- 12 Flares ×

Sign up for updates!

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.
12 Flares Twitter 0 Facebook 0 Google+ 3 LinkedIn 9 Email -- 12 Flares ×