How to create stateful Alloy widgets

Stateful widgets

CommonJS modules are stateful. They are loaded into memory once on their first use after which every next require() will return the same instance. This makes them perfect to act as global configuration modules or event dispatchers.

NOTE: As pointed out by Timan in the comments, in Titanium this only works if you use the exact same path each time you require. Using require('/a') and then require('a') will give you two instances.

Alloy Controlers & Widgets instances

Alloy Controllers and Widgets both use CommonJS modules. If you look at the generated code, you’ll see them exporting a Controller() function, which is then used to instanciate the controller using new in alloy.js:

exports.createWidget = function(id, name, args) {
    if ("undefined" != typeof name && null !== name && _.isObject(name) && !_.isString(name)) {
        args = name;
        name = DEFAULT_WIDGET;
    }
    return new (require("alloy/widgets/" + id + "/controllers/" + (name || DEFAULT_WIDGET)))(args);
};

Because all your original controller-code is wrapped by this Controller() function, you are notable to export other variables or use private stateful variables shared between differenced instances of the same controller/widget.

Widget libraries

The solution is to put any stateful variables your widget instances need access to in a seperate CommonJS module and require() that in the widget’s Controller() code. Widgets can have assets and lib folders just like Alloy apps can so the code would still be nicely contained in the widget.

Theoretical example

Let’s see how this works by looking at a simple widget that displays a label with an incrementing value; the hello world of statefulness. The first instance of the widget will be a label showing 1, the next one will have 2 etc.

app/widgets/my.widget/views/widget.xml

<Alloy>
    <Label id="count" />
</Alloy>

app/widgets/my.widget/controllers/widget.js

var stateful = require(WPATH('stateful'));

$.count.text = stateful.count.toString();

stateful.count++;

NOTE: The WPATH() function will return a path relative to the widget folder.

app/widgets/my.widget/lib/stateful.js

module.exports = {
    count: 1
};

Real-world example

A real-world example is the button widget I did a year ago. The Adding styles and setting the default section of the README explains how a seperate styles.js CommonJS module can be used to set and change built-in and custom button-styles and set the default a new instance of the widget will use.

App imagineer: Imagining, Engineering & Speaking about Native mobile Apps with Appcelerator Titanium & Alloy • Meetup organizer • Certified Expert • Titan


Comments