Slide 1

Slide 1 text

July 23rd, 2013 London Titanium Alloy Tips & Tricks FOKKE ZANDBERGEN app imagineer

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

               Alloy Tips & Tricks

Slide 4

Slide 4 text

Compilation process Not your mother's MVC TSS XML Views + = Definition = Models Controllers Assets config.json

Slide 5

Slide 5 text

Compilation process Not your mother's MVC Definition = Models Controllers Assets config.json JS

Slide 6

Slide 6 text

Compilation process Not your mother's MVC Definition = Models Controllers Assets config.json JS CommonJS

Slide 7

Slide 7 text

Compilation process Not your mother's MVC Collections Sync Store Models Controllers Assets config.json JS CommonJS

Slide 8

Slide 8 text

Compilation process Not your mother's MVC Collections Sync Store Models Controllers Assets config.json JS CommonJS

Slide 9

Slide 9 text

Compilation process Not your mother's MVC Collections Sync Store Models Controllers Assets config.json Alloy.CFG JS CommonJS

Slide 10

Slide 10 text

Compilation process Not your mother's MVC Collections Sync Store Models Controllers Assets config.json Alloy.CFG JS CommonJS

Slide 11

Slide 11 text

Compiled controllers The first might be last function Controller() { ! // Controller functions function myFoo() { alert("Says bar"); } var $ = this; var exports = {}; ! // Parsed XML view $.__views.label = Ti.UI.createLabel({ id: "label" }); $.__views.label && $.addTopLevelView($.__views.label); _.extend($, $.__views); ! // Controler code $.myLabel.text = "Hello London!"; exports.myFoo = myFoo; _.extend($, exports); } var Alloy = require("alloy"), Backbone = Alloy.Backbone, _ = Alloy._; module.exports = Controller;

Slide 12

Slide 12 text

Compiled controllers About $, exports and this $.label === $.__views.label === this.label; exports.label === undefined; exports.foo = 'bar'; $.bar = 'foo'; Inline code function myFunction() { ! $.label === $.__views.label; ! this.label === exports.label === undefined; ! ! $.foo === exports.foo; ! this.foo = undefined; exports.bar === undefined; } Functions

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

Titanium Studio Shortcuts Be lazy ⇧⌘C New controller + view + style ⇧⌘Y New widget ⇧⌘M New model ⇧⌘I New migration More: http://go.fokkezb.nl/ttshortcuts

Slide 15

Slide 15 text

Installing Alloy from Github Living on the edge sudo npm install -g git://github.com/appcelerator/alloy.git Rollback to stable sudo npm uninstall -g alloy sudo npm install -g alloy * Drop "sudo" on Windows

Slide 16

Slide 16 text

Hacking Alloy Have the last word /app/assets/alloy.js /app/assets/alloy/backbone.js /app/assets/alloy/underscore.js /app/assets/alloy/sync/sql.js /app/themes/myTheme/assets/alloy/CFG.js • The SQLite sync adapter is not compatible with Backbone.js 1.x • Make sure you sync config.json changes to your CFG.js files Alloy.isTablet = function() { return (Ti.Platform.displayCaps.platformWidth > 800); } app/alloy.js .. or go wild and overwrite Extend via app.js

Slide 17

Slide 17 text

WPATH Which Path | | Widget Path function WPATH(s) { var index = s.lastIndexOf("/"); var path = -1 === index ? "myWidget/" + s : s.substring(0, index) + "/myWidget/" + s.substring(index + 1); return path; } app/widgets/myWidget/assets/foo.png Resources/myWidget/foo.png app/widgets/myWidget/assets/one/two/three.png Resources/one/two/MyWidget/three.png Examples • Assets and libs the same • Place stuff in platform specific iphone, android folders • Place stuff in the images folder

Slide 18

Slide 18 text

Custom sync adapters .. for widget models /app/widgets/my.widget/assets/alloy/sync/myAdapter.js Drop your sync adapter in: exports.definition = { config: { adapter: { type: 'my.widget/myAdapter' } } }; Use in widget models: /Resources/alloy/sync/my.widget/myAdapter.js It will end up in: /app/widgets/my.widget/models/myModel.js

Slide 19

Slide 19 text

All-in-one widget Know the 'name' Bundle a group of widgets as one * Available since Alloy 1.1 Specific controller per context Alloy.createWidget("nl.fokkezb.tweetsView", "widget", { id: "myWidget", foo: "bar" });

Slide 20

Slide 20 text

Styleable & Themable widgets The . and # game Set ID and class of view components to same, globally unique value ! Apply defaults styles using classes ".myWidgetLabel": { ! color: 'red' } "#myWidgetLabel": { ! color: 'blue' } Instruct developers using your widget to override using IDs More: http://go.fokkezb.nl/alloytssprio (widgets missing)

Slide 21

Slide 21 text

__parentSymbol Who's your daddy? ! ! ! ! __parentSymbol.backgroundColor = '#'+Math.floor(Math.random()*16777215).toString(16); app/widgets/randomBgColor/controllers/widget.js

Slide 22

Slide 22 text

Stateful widgets Keeping track ! ! ! ! ! ! var state = require(WPATH('state')); $.instanceCounter.text = state.counter++; exports.counter = 0; app/widgets/stateful/assets/state.js app/widgets/stateful/controllers/widget.js app/views/index.xml

Slide 23

Slide 23 text

Data Binding 3 flavours var $model = arguments[0] ? arguments[0]["$model"] : null; $.instance = Alloy.createModel("myModel"); $.__views.a.text = "undefined" != typeof $model.__transform["field"] ? $model.__transform["field"] : $model.get("field"); var __alloyId1 = function() { $.b.text = _.isFunction(Alloy.Models.singleton.transform) ? Alloy.Models.singleton.transform()["field"] : Alloy.Models.singleton.get("field"); }; Alloy.Models.singleton.on("fetch change destroy", __alloyId1); var __alloyId2 = function() { $.c.text = _.isFunction($.instance.transform) ? $.instance.transform()["field"] : $.instance.get("field"); }; $.instance.on("fetch change destroy", __alloyId2);

Slide 24

Slide 24 text

Bind an existing model Workarounds function openDetail(e) { ! var model = col.get(e.rowData.modelId); ! model.__transform = myTransformer(model); // model.toJSON(); ! Alloy.createController('detail', { ! ! '$model': model ! }); } function openDetail(e) { ! var detailModel = col.get(e.rowData.modelId); ! detailModel.__transform = myTransformer(detailModel); ! detailModel.transform = function() { return this.__transform; }; ! Alloy.Models.detailModel = detailModel; ! Alloy.createController('detail'); } Pre-setting a singleton Emulating a dataCollection loop WARNING: No change listeners

Slide 25

Slide 25 text

Main controller switching Skipping index.js var Alloy = require("alloy"), _ = Alloy._, Backbone = Alloy.Backbone; Alloy.createController("index"); • Alloy always requires index.xml app.js // $.index.open(); if (Ti.App.Properties.getBool('isLoggedIn', false)) { Alloy.createController("home"); } else { Alloy.createController("login"); } index.js index.xml

Slide 26

Slide 26 text

Reset your app.tss Clean slate 'Label[platform=android]': { // Instead of platform-dependent color: '#000' } 'Window': { // More common-sense default vs 'transparent' backgroundColor: '#fff' } 'Window[platform=android]': { // Never lightweight (deprecated) modal: false } 'TextField': { // More common-sense default vs un-styled borderStyle: Ti.UI.INPUT_BORDERSTYLE_ROUNDED } 'ImageView[platform=ios]': { // Never show ugly temporary image preventDefaultImage: true } } Discussion: http://go.fokkezb.nl/alloyreset

Slide 27

Slide 27 text

@import TSS Or the power of alloy.jmk @import "reset.tss"; "Window": { ! backgroundColor: 'black' } Source: http://go.fokkezb.nl/alloyimport task("pre:compile", function(event, logger) { ! explodeImport(event, logger); }); task("post:compile", function(event, logger) { ! implodeImport(event, logger); }); app/alloy.jmk

Slide 28

Slide 28 text

Jade 2 XML Be lazy like David Bankier Hello, World Blog: http://go.fokkezb.nl/alloyjade Alloy Window.container Label#label(onClick="doClick") Hello, World app/views/index.jade • No need for closing tags • Advanced templating • Advanced conditions

Slide 29

Slide 29 text

XML 2 TSS Be lazy like David Bankier Hello, World Source: https://github.com/dbankier/xml2tss ".container": {} ".#label": {} xml2tss index.xml ../styles/index.tss • Will update existing TSS • Integrated in Alloy 1.2

Slide 30

Slide 30 text

Less TSS Be lazy like David Bankier @primColor: "blue"; .bg (@color) { backgroundColor: $color } ".container" : { .bg($primColor); } Source: https://github.com/dbankier/ltss ".container" : { backgroundColor: "blue" } ltss index.ltss index.tss

Slide 31

Slide 31 text

Detecting Alloy Using Alloy libs without Alloy var isAlloy = (typeof ENV_TEST === 'boolean'); • Alloy constants like OS_IOS don't exist at run-time • They are replaced by TRUE|FALSE during compile • Code blocks that will never execute are removed • In Vanilla Titanium they will be undefined var Alloy = require('alloy'); Alloy.infect && Alloy.infect(this); myLib.js or app.js (3.1+) Source: http://go.fokkezb.nl/alloydetect

Slide 32

Slide 32 text

Wrapping Ti.UI.* components .. using widgets or CommonJS modules Before After: Coming in Alloy 1.2: • Pass on or intercept common parameters, functions and events /app/widgets/ImageView/controllers/widget.js require('My.UI').createImageView() Simple, durable workaround:

Slide 33

Slide 33 text

Wrapping Ti.UI.* components Passing arguments exports.setText = function (text) { $.myVew.text = text.toUpperCase(); } _.each(['text', 'color'], function (pr) { var cam = pr[0].toUpperCase() + pr.substring(1); var get = exports['get' + cam] || ($['get' + cam] = function () { retur $.myVew[pr]; }); var set = exports['set' + cam] || ($['set' + cam] = function (val) { $.myVew[pr] = val; }); Object.defineProperty($, pr, { get: get, set: set }); }); exports.applyProperties = function(properties) { properties = _.omit(properties, 'id', '__parentSymbol', '__itemTemplate', '$model'); var apply = {}; _.each(properties, function (val, pr) { ! var fn = 'set' + pr[0].toUpperCase() + pr.substring(1); ! exports[fn] ? exports[fn](val) : (apply[pr] = val); }); _.isEmpty(apply) || $.myVew.applyProperties(apply); } exports.applyProperties(arguments[0]); • Looping _.keys($.myVew) would only give set properties • Add set/get on $ as so applyProperties can't find them • Define properties on $ as that's the final object • Filter Alloy properties as (__parentSymbol would give memory leak)

Slide 34

Slide 34 text

Wrapping Ti.UI.* components Passing functions _.each(['resume', 'pause', 'hide'], function (fn) { if (!exports[fn]) { ! exports[fn] = $.myVew[fn]; } }); • Remember we already did setters and getters for properties

Slide 35

Slide 35 text

Wrapping Ti.UI.* components Passing events exports.on = $.myVew.addEventListener; exports.off = $.myVew.removeEventListener; exports.trigger = $.myVew.fireEvent; • For (widget) controllers onClick translates to .on('click')

Slide 36

Slide 36 text

Resources Must reads • Alloy docs http://go.fokkezb.nl/alloydocs • Backbone.js 0.9.2 http://go.fokkezb.nl/bb092 • Underscore.js http://go.fokkezb.nl/undscore • Moment.js http://go.fokkezb.nl/momentjs • Alloy Google Group http://go.fokkezb.nl/alloygroup • Alloy Q&A http://go.fokkezb.nl/alloyqa • Alloy changelog http://go.fokkezb.nl/alloylog • Alloy test apps http://go.fokkezb.nl/alloytests • Alloy compiler source http://go.fokkezb.nl/alloycompiler • Alloy widgets @ AlloyLove.com • Twitter: #TiAlloy http://go.fokkezb.nl/alloylinks

Slide 37

Slide 37 text

Questions? [email protected] www.FokkeZB.nl @FokkeZB