29
ALLOY WIDGETS Improving code re-use through a library of bespoke UI components. TiConf.eu Martin Hudson, Jonti Hudson February 2013

Creating Alloy Widgets

Embed Size (px)

DESCRIPTION

Create Alloy widgets using Titanium Appcelerator.

Citation preview

Page 1: Creating Alloy Widgets

ALLOY WIDGETSImproving code re-use through a library of bespoke UI

components.

TiConf.euMartin Hudson, Jonti Hudson

February 2013

Page 2: Creating Alloy Widgets

WHAT IS A WIDGET?

A self-contained bespoke UI component that holds all the logic associated with its use.

Page 3: Creating Alloy Widgets

WHAT IS A WIDGET?

A self-contained bespoke UI component that holds all the logic associated with its use.

• Create a re-usable library across multiple projects

Page 4: Creating Alloy Widgets

WHAT IS A WIDGET?

A self-contained bespoke UI component that holds all the logic associated with its use.

• Create a re-usable library across multiple projects

• Create components that manage cross-platform differences (e.g. a table edit / delete component)

Page 5: Creating Alloy Widgets

WHAT IS A WIDGET?

A self-contained bespoke UI component that holds all the logic associated with its use.

• Create a re-usable library across multiple projects

• Create components that manage cross-platform differences (e.g. a table edit / delete component)

• Improve readability of code

Page 6: Creating Alloy Widgets

WHAT IS A WIDGET?

A self-contained bespoke UI component that holds all the logic associated with its use.

• Create a re-usable library across multiple projects

• Create components that manage cross-platform differences (e.g. a table edit / delete component)

• Improve readability of code

• Improve reliability due to re-use of tested components.

Page 7: Creating Alloy Widgets

WHAT IS A WIDGET?

App Widget

Multiple widgets in the same window...

Page 8: Creating Alloy Widgets

... or multiple instances of the same widget

WHAT IS A WIDGET?

App Widget

Page 9: Creating Alloy Widgets

A CUSTOM TABLE VIEW WIDGET

An example of building a cross-platform widget that encapsulates common functionality but utilises platform specific behaviour.

Page 10: Creating Alloy Widgets

CREATING A WIDGETTo create a new widget, right click the project name in the Titanium Studio project view... and select New → Alloy Widget.

Page 11: Creating Alloy Widgets

CREATING A WIDGETTo create a new widget, right click the project name in the Titanium Studio project view... and select New → Alloy Widget.

Use a “Reverse domain” naming convention to ensure widget names are not replicated when you share widgets with others.

Page 12: Creating Alloy Widgets

WIDGET FILE STRUCTURE

Note the absence of Models – this is because the main app should handle data storage

A widgets folder is created with Controllers, Styles and Views sub-directories.

Page 13: Creating Alloy Widgets

Widget – widget.xml

USING A WIDGET

App – index.xml

<Alloy><Window class="container"> <Require type="widget" src="co.mobiledatasystems.customEditableTable" id="table1"> </Require> <Button id="btnEdit" title="Allow Editing" onClick="btnEdit_click_Event"> </Button></Window></Alloy>

<Alloy> <TableView id="table"></TableView></Alloy>

In the main app, we will call the newly created widget using the “Require” tag.

Page 14: Creating Alloy Widgets

Widget – widget.xml

USING A WIDGET

App – index.xml

<Alloy><Window class="container"> <Require type="widget" src="co.mobiledatasystems.customEditableTable" id="table1"> </Require> <Button id="btnEdit" title="Allow Editing" onClick="btnEdit_click_Event"> </Button></Window></Alloy>

<Alloy> <TableView id="table"></TableView></Alloy>

Note we specify that we want a widget and reference the name of our new widget. Alloy knows where to find it.

In the main app, we will call the newly created widget using the “Require” tag.

Page 15: Creating Alloy Widgets

Widget – widget.xml

USING A WIDGET

App – index.xml

<Alloy><Window class="container"> <Require type="widget" src="co.mobiledatasystems.customEditableTable" id="table1"> </Require> <Button id="btnEdit" title="Allow Editing" onClick="btnEdit_click_Event"> </Button></Window></Alloy>

<Alloy> <TableView id="table"></TableView></Alloy>

In the main app, we will call the newly created widget using the “Require” tag.

In the widget, we specify the UI components we want to expose. In our case it is only a TableView.

Page 16: Creating Alloy Widgets

STYLINGWidget – widget.tssApp – index.tss

We have referenced our instance of the widget “table1” in index.xml

".container": { backgroundColor:"white"},"#table1": { left: '10dp', right: '10dp', top: '20dp', bottom:'80dp'},"#btnEdit": { bottom:'10dp', left:'20dp', right:'20dp', height:'45dp'}

Page 17: Creating Alloy Widgets

Note, where possible, do all the styling of widget components in the main app. This ensures maximum re-usability across projects.

STYLINGWidget – widget.tssApp – index.tss

".container": { backgroundColor:"white"},"#table1": { left: '10dp', right: '10dp', top: '40dp', bottom:'80dp'},"#btnEdit": { bottom:'10dp', left:'20dp', right:'20dp', height:'45dp'}

Page 18: Creating Alloy Widgets

".container": { backgroundColor:"white"},"#table1": { left: '10dp', right: '10dp', top: '40dp', bottom:'80dp'},"#btnEdit": { bottom:'10dp', left:'20dp', right:'20dp', height:'45dp'}

CONTROLLERS - INITIALISEWidget – widget.jsApp – index.tss

In the widget's controller we can access all the parameters passed in using the arguments[] array.

//copy the arguments passed in to the widget via. the xml and tss parameters

var _args = arguments[0] || {};

var editable = null;

if(OS_ANDROID){editable = false;};

//get each element set in the widget's xml or tss parameters

Ti.API.info(JSON.stringify(_args));

//iterate round all the parameters we have passed in

for (var key in _args) { if (_args.hasOwnProperty(key)) {

//checks key is a direct property of _args, not somewhere down the object tree

if(OS_ANDROID){ switch (key){

Page 19: Creating Alloy Widgets

".container": { backgroundColor:"white"},"#table1": { left: '10dp', right: '10dp', top: '40dp', bottom:'80dp'},"#btnEdit": { bottom:'10dp', left:'20dp', right:'20dp', height:'45dp'}

CONTROLLERS - INITIALISEWidget – widget.jsApp – index.tss

In the widget's controller we can access all the parameters passed in using the arguments[] array.

//copy the arguments passed in to the widget via. the xml and tss parameters

var _args = arguments[0] || {};

var editable = null;

if(OS_ANDROID){editable = false;};

//get each element set in the widget's xml or tss parameters

Ti.API.info(JSON.stringify(_args));

//iterate round all the parameters we have passed in

for (var key in _args) { if (_args.hasOwnProperty(key)) {

//checks key is a direct property of _args, not somewhere down the object tree

if(OS_ANDROID){ switch (key){

Page 20: Creating Alloy Widgets

".container": { backgroundColor:"white"},"#table1": { left: '10dp', right: '10dp', top: '40dp', bottom:'80dp'},"#btnEdit": { bottom:'10dp', left:'20dp', right:'20dp', height:'45dp'}

CONTROLLERS - INITIALISEWidget – widget.jsApp – index.tss

We can read each parameter passed in and process them appropriately. In our example “editable” and “moving” are iOS specific. We will set a local variable in the case of Android.

//iterate round all the parameters we have passed in

for (var key in _args) { if (_args.hasOwnProperty(key)) {

//checks key is a direct property of _args, not somewhere down the object tree

if(OS_ANDROID){ switch (key){ case 'editing': editable = _args[key]; break; case 'moving': break; //android doesn't recognise this property default: $.table[key] = _args[key]; } } else { $.table[key] = _args[key]; }; };};

Page 21: Creating Alloy Widgets

".container": { backgroundColor:"white"},"#table1": { left: '10dp', right: '10dp', top: '40dp', bottom:'80dp'},"#btnEdit": { bottom:'10dp', left:'20dp', right:'20dp', height:'45dp'}

CONTROLLERS - INITIALISEWidget – widget.jsApp – index.tss

We can read each parameter passed in and process them appropriately. In our example “editable” and “moving” are iOS specific. We will set a local variable in the case of Android.

//iterate round all the parameters we have passed in

for (var key in _args) { if (_args.hasOwnProperty(key)) {

//checks key is a direct property of _args, not somewhere down the object tree

if(OS_ANDROID){ switch (key){ case 'editing': editable = _args[key]; break; case 'moving': break; //android doesn't recognise this property default: $.table[key] = _args[key]; } } else { $.table[key] = _args[key]; }; };};

Page 22: Creating Alloy Widgets

$.index.open();

var editMode = false;

//create some data to put in the tablevar rows = [];for(var i=0;i<10;i++){rows.push(Ti.UI.createTableViewRow({title:'Row number ' + i}));};$.table1.setData(rows); //call the "setData" method we created in the widget

//toggle the "editable" mode of the tablefunction btnEdit_click_Event(){ if(editMode){ $.btnEdit.title = "Allow Editing"; editMode = false; } else { $.btnEdit.title = "Cancel Editing"; editMode = true; }; $.table1.editing(editMode); //call the "editing" method we created in the widget};

CONTROLLERS – CALLING FUNCTIONSWidget – widget.jsApp – index.js

Using the commonJS framework, we can expose functions in the widget. In our example we expose “setData” so we can populate the table after the widget has created it.

//custom method we expose to set the table's dataexports.setData = function(rows /*Ti.UI.Row*/){ $.table.setData(rows);};

//custom method we expose to allow the table to be editableexports.editing = function(edit /*bool*/){ if(OS_IOS){ $.table.editing = edit; //allow row editing on iPhone & iPad } else { editable = edit; };};

Page 23: Creating Alloy Widgets

$.index.open();

var editMode = false;

//create some data to put in the tablevar rows = [];for(var i=0;i<10;i++){rows.push(Ti.UI.createTableViewRow({title:'Row number ' + i}));};$.table1.setData(rows); //call the "setData" method we created in the widget

//toggle the "editable" mode of the tablefunction btnEdit_click_Event(){ if(editMode){ $.btnEdit.title = "Allow Editing"; editMode = false; } else { $.btnEdit.title = "Cancel Editing"; editMode = true; }; $.table1.editing(editMode); //call the "editing" method we created in the widget};

CONTROLLERS – CALLING FUNCTIONSWidget – widget.jsApp – index.js

Using the commonJS framework, we can expose functions in the widget. In our example we expose “setData” so we can populate the table after the widget has created it.

//custom method we expose to set the table's dataexports.setData = function(rows /*Ti.UI.Row*/){ $.table.setData(rows);};

//custom method we expose to allow the table to be editableexports.editing = function(edit /*bool*/){ if(OS_IOS){ $.table.editing = edit; //allow row editing on iPhone & iPad } else { editable = edit; };};

Page 24: Creating Alloy Widgets

$.index.open();

var editMode = false;

//create some data to put in the tablevar rows = [];for(var i=0;i<10;i++){rows.push(Ti.UI.createTableViewRow({title:'Row number ' + i}));};$.table1.setData(rows); //call the "setData" method we created in the widget

//toggle the "editable" mode of the tablefunction btnEdit_click_Event(){ if(editMode){ $.btnEdit.title = "Allow Editing"; editMode = false; } else { $.btnEdit.title = "Cancel Editing"; editMode = true; }; $.table1.editing(editMode); //call the "editing" method we created in the widget};

CONTROLLERS – CALLING FUNCTIONSWidget – widget.jsApp – index.js

Here, the main app is responding to a button click event and changing the editable state in the widget.

//custom method we expose to set the table's dataexports.setData = function(rows /*Ti.UI.Row*/){ $.table.setData(rows);};

//custom method we expose to allow the table to be editableexports.editing = function(edit /*bool*/){ if(OS_IOS){ $.table.editing = edit; //allow row editing on iPhone & iPad } else { editable = edit; };};

Page 25: Creating Alloy Widgets

$.index.open();

var editMode = false;

//create some data to put in the tablevar rows = [];for(var i=0;i<10;i++){rows.push(Ti.UI.createTableViewRow({title:'Row number ' + i}));};$.table1.setData(rows); //call the "setData" method we created in the widget

//toggle the "editable" mode of the tablefunction btnEdit_click_Event(){ if(editMode){ $.btnEdit.title = "Allow Editing"; editMode = false; } else { $.btnEdit.title = "Cancel Editing"; editMode = true; }; $.table1.editing(editMode); //call the "editing" method we created in the widget};

CONTROLLERS – CALLING FUNCTIONSWidget – widget.jsApp – index.js

Here, the main app is responding to a button click event and changing the editable state in the widget.

//custom method we expose to set the table's dataexports.setData = function(rows /*Ti.UI.Row*/){ $.table.setData(rows);};

//custom method we expose to allow the table to be editableexports.editing = function(edit /*bool*/){ if(OS_IOS){ $.table.editing = edit; //allow row editing on iPhone & iPad } else { editable = edit; };};

Page 26: Creating Alloy Widgets

//create a handlers object that will contain references to functionsvar handlers = {};

//assign some functions that do nothing.handlers.click = function(r){};handlers.deleteRow = function(r){};

//expose a function that can pass in a reference to an external function and assign the reference to the appropriate handler.exports.addEventListener = function(listenerName, listenerFunction){ switch (listenerName){ case 'click' : handlers.click = listenerFunction; break; case 'delete' : handlers.deleteRow = listenerFunction; break; };};

if(OS_IOS){ $.table.addEventListener('delete', function(r){

$.table1.addEventListener('delete', function(r){Ti.API.info('row deleted: ' + JSON.stringify(r));var dialog = Ti.UI.createAlertDialog({ message: r.row.title, title: 'Deleted' }); dialog.show();});

CONTROLLERS – HANDLING EVENTSWidget – widget.jsApp – index.js

In the widget we create an “addEventListener” function that has two parameters, the name of the event and a reference to the function it will call in the main app.

We assign the reference to a “handlers” object we created in the widget.

Page 27: Creating Alloy Widgets

//create a handlers object that will contain references to functionsvar handlers = {};

//assign some functions that do nothing.handlers.click = function(r){};handlers.deleteRow = function(r){};

//expose a function that can pass in a reference to an external function and assign the reference to the appropriate handler.exports.addEventListener = function(listenerName, listenerFunction){ switch (listenerName){ case 'click' : handlers.click = listenerFunction; break; case 'delete' : handlers.deleteRow = listenerFunction; break; };};

if(OS_IOS){ $.table.addEventListener('delete', function(r){

$.table1.addEventListener('delete', function(r){Ti.API.info('row deleted: ' + JSON.stringify(r));var dialog = Ti.UI.createAlertDialog({ message: r.row.title, title: 'Deleted' }); dialog.show();});

CONTROLLERS – HANDLING EVENTSWidget – widget.jsApp – index.js

In the main app we call the “addEventListener” function, passing in the name of the listener and defining the function we want to execute when the even is fired.

Page 28: Creating Alloy Widgets

//assign some functions that do nothing.handlers.click = function(r){};handlers.deleteRow = function(r){};

//expose a function that can pass in a reference to an external function and assign the reference to the appropriate handler.exports.addEventListener = function(listenerName, listenerFunction){ switch (listenerName){ case 'click' : handlers.click = listenerFunction; break; case 'delete' : handlers.deleteRow = listenerFunction; break; };};

if(OS_IOS){ $.table.addEventListener('delete', function(r){ handlers.deleteRow(r); });};

$.table1.addEventListener('delete', function(r){Ti.API.info('row deleted: ' + JSON.stringify(r));var dialog = Ti.UI.createAlertDialog({ message: r.row.title, title: 'Deleted' }); dialog.show();});

CONTROLLERS – HANDLING EVENTSWidget – widget.jsApp – index.js

In the widget we listen for the table's “delete” event and call the appropriate handler object.

Page 29: Creating Alloy Widgets

THANK YOU

Source code: http://bit.ly/alloy-customTableView

Slideshare: http://bit.ly/alloy-customTableViewSlides

Mobile Data Systems Ltd.Turnkey mobile consultancy

www.mobiledatasystems.co

[email protected]