Reactive UI framework for Titanium Alloy
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
dist
sample
.gitignore
.npmignore
LICENSE
README.md
manifest
package.json
staballoy.js

README.md

Staballoy

Reactive UI framework for Titanium Alloy with event handling

Why the name staballoy? Well.. to quote wikipedia

staballoys are metal alloys of a high proportion of depleted uranium with other metals, usually titanium

It has the Titanium in it, is an alloy and uranium is a reactive metal. Sounds like the perfect name to me.

How does it work?

Staballoy injects itself in every controller and can handle monitoring of variables for you in a reactive way. You can subscribe any UI component in staballoy and your UI component will be updated for the argument you provide.

Setup

So enough talk, how do I install it? First download the latest release (from the dist folder) and install it as a commonjs module.

<modules>
    <module platform="commonjs" version="0.1">staballoy</module>
</modules>

Through NPM

In classic go to the Resources folder, for Alloy go to the lib directory of your project, then run the following command

npm install staballoy Note: if your app doesn't have node_modules yet, run npm init first!

Include it in your application

Next, require staballoy in your alloy.js file.

var staballoy = require('staballoy');

Now, this will override Alloy.createController so it is not compatible with any other modules/libraries that do the same. Keep this in mind! (barely any modules/library does this, but Jason Kneens AlloyXL is an example)

Next, staballoy requires a newer underscorejs lib to function, you can do so by downloading the latest underscorejs and putting that in /lib/alloy/underscore.js. You can follow this ticket on JIRA to monitor when that is done by default.

Supported controllers

Every Window controller will have access to a $.args.subscribe method as well as $.args.staballoy which contains methods to set and get variables stored. The current supported controller types are

  • Ti.UI.Window
  • Ti.UI.TabGroup
  • Ti.UI.iOS.NavigationWindow

Staballoy automatically removes subscriptions based on the close event of these UI elements. If it wouldn't be able to do this there would be memory leaks. It is important to only use Windows that are created by alloy!

Variables

You can set variables stored in staballoy. Any variable set are single session. There is no storage. Therefore staballoy should not be used as storage. Keep that in mind when using it.

You can set and get variables in any Alloy created controller, as it will have the following methods exposed:

$.args.staballoy.setVar('myVarName', 'value');
var value = $.args.staballoy.getVar('myVarName');

Thats all you need to do to update variables. Again, if you want to store the data you should handle the storage of it, and init the right data when starting the app, in alloy.js for example, or better in a library file (eg. /lib/initStaballoy.js) called from alloy.js

Deep setting/getting

If you're storing objects in variables, you can update them without having to get them first. For example, if you have this object:

var config = {
    "user": {
        "preferences": {
            "notifications": true,
            "language": "en"
        }
    }
}
staballoy.setVar('config', config);

If you now want to update the language, you can do it like this:

staballoy.setVar('config_user_preferences_language', 'de');

Same with getting it.

staballoy.getVar('config_user_preferences_language');
// results in "de"

You can even set properties on object trees that don't exist yet, and it will make the tree for you

staballoy.setVar('config_user_info_name', 'myName');

Subscribe

Any of the supported controllers will be automatically parsed and checked for subscriptions. These subscriptions can be configured in the tss file. Say we have a label

<Label id="myLabel" />

And you want to subscribe the text property to the variable closeButtonName. You can do that by putting the following in tss

    "#myLabel": {
        staballoy: {
            subscriptions: {
                "closeButtonName": "text"
            }
        }
    }

The subscriptions object specifies what to subscribe to and to what attribute. In this example I am subscribing to the closeButtonName variable, and I want the text attribute to be set with the value that is contained in the variable. Multiple subscriptions are possible for every element.

Internally, every time the closeButtonName variable is updated, the following code is executed: $.myLabel.text = closeButtonName;

As with setVar and getVar you can also use deep-binding and it will get it from inside the object

    "#myLabel": {
        staballoy: {
            subscriptions: {
                "config_user_info_name": "text"
            }
        }
    }

2-way data-binding

When you subscribe to a property with the value binding, it will automatically be 2-way. The tss sample below is of a textfield.

staballoy: {
    subscriptions: {
        "user_name": "value"
    }
},

So when the value of the textfield changes, using the change event, the user_name variable will be updated automatically as well. So when you bind it to a label somewhere else, that will update automatically too.

Manual Subscribe

If you don't want to subscribe in the tss but want to do it more dynamically, in the controller, you can do it as follows:

$.args.subscribe({
    'component' : $.myLabel,
    'window' : $,
    'subscriptions' : {'closeButtonName': 'text'},
});

The object consists of:

  • component - The UI element you want updated
  • window - The window the UI element is in, this needs to be an alloy generated window, so usually $ is enough. The window GUID is also accepted
  • subscriptions - As explained above.

As you can see this flow is the same as the earlier one, but it is a little more complex as it needs to know the context.

Using setters

Instead of using an attribute, you can also use the setters. So if you provide the following for subscriptions:

    'subscriptions' : {'closeButtonName': 'setText'},

This will internally be translated to: $.myLabel.setText(closeButtonName)

Manipulating the data that is being set

So want to alter the data of the variable before it being set you can use the logicFunction. It can be really powerfull if you store objects in staballoy. You can do that as following:

$.args.staballoy.setVar('closeButtonStuff', {"id" : 5, "key": "buttonTitle"});

You then set the logicFunction has a string. This is of course a property in the tss, but can also be specified in the manual flow

logicFunction: "myLogicFunction"

Then, you can add the logicFunction to the $ nameSpace in the controller

$.myLogicFunction = function(value){
	return L(value.key);
};

In the manual approach you can, instead of a string to the $ namespace, you can also add in the function directly, so it can also be an anonymous function

logicFunction: function(value){
    return L(value.key);
}

Now, you can use part of an object to set the value of your UI component automatically. Another example for visibilty of an object based on an integer you can find in the sample app.

Subscribe in other controllers

The preferred method would be to pass the window GUID to the child controller, so you can pass the guid in the manual subscribe method inside a sub-controller

// index.js
Alloy.createController('mySubController', {parentGuid: $.args.staballoy.guid});

// mySubController
$.args.subscribe({
    window: $.args.parentGuid
});

In case you create the controller inside an XML, you should create a setter and start subscribing after the setter has been called.

You could also use an exported function in the sub-controller, and use the main controller (a window) to subscribe. An example of this is included in the sample app.

// index.js
$.args.subscribe({
    'component' : $.subView,
    'window' : $,
    'subscriptions' : {'closeButtonName': 'setVisible'}
});

$.subView is a <Require> in index.xml.

	<Require src="subView" id="subView"></Require>

As I'm using the setVisible property, I can have an exported function in subView.js so that can be called.

exports.setVisible = function(value){
    $.getView().visible = value;
};

This flow is not really optimal so expect it to change in the future with a non-backwards compatible implementation. Suggestions for the new flow are welcome.

Missing features / bugs / questions?

For missing features and bugs, please submit a ticket, or even better submit a Pull Request.

If you have any questions, just contact me on TiSlack. My name there is @Wraldpyk. There also is a #staballoy channel.

Want to support my work? Send a donation my way via the PayPal button on TiSlack.org. Any support is appreciated.

Changelog

  • 0.3.3 - (20180905) Fixed issue where creating empty controllers crashed the app
  • 0.3.0 - (20180711) Added 2-way binding and deep-binding.
  • 0.2.4 - (20171205) Add the ability to manual subscribe with a guid instead of a window.
  • 0.2.3 - (20171030) Ignoring the setVar function call if the new value is the same as the stored one
  • 0.2.2 - (20171024) Subscriptions of top most element weren't processed. Now they are!
  • 0.2.1 - (20171019) Added support for NavigationWindows & ListSections. Fixed elements with children not being able to subscribe
  • 0.2 - (20171012) Added direct subscribers from tss instead of controllers
  • 0.1 - (20170928) First release