Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Alloy Tips & Tricks #TiLon

Alloy Tips & Tricks #TiLon

Overview of Appcelerator Titanium Alloy tips, tricks and hacks from the talk I did at London Titanium on July 23rd.

Read the blog at: http://fokkezb.nl/2013/07/23/alloy-tips-tricks/

Fokke Zandbergen

July 23, 2013
Tweet

More Decks by Fokke Zandbergen

Other Decks in Technology

Transcript

  1.          

         Alloy Tips & Tricks
  2. Compilation process Not your mother's MVC TSS XML Views +

    = Definition = Models Controllers Assets config.json
  3. Compilation process Not your mother's MVC Collections Sync Store Models

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

    Controllers Assets config.json Alloy.CFG JS CommonJS
  5. 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;
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. All-in-one widget Know the 'name' Bundle a group of widgets

    as one <Widget src="myMenus" /><!-- default: widget(.js) --> <Widget src="myMenus" name="slide" /> <Widget src="myMenus" name="drawer" /> * Available since Alloy 1.1 Specific controller per context <Widget src="myList" /><!-- default: widget(.js) --> <Widget src="myList" name="tab" /> <Widget src="myList" name="window" /> <Widget src="myList" name="view" /> Alloy.createWidget("nl.fokkezb.tweetsView", "widget", { id: "myWidget", foo: "bar" });
  13. Styleable & Themable widgets The . and # game Set

    ID and class of view components to same, globally unique value <Alloy> ! <Label id="myWidgetLabel" class="myWidgetLabel" /> </Alloy> 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)
  14. __parentSymbol Who's your daddy? <Alloy> ! <Window> ! ! <Widget

    src="randomBgColor" /> ! </Window> </Alloy> __parentSymbol.backgroundColor = '#'+Math.floor(Math.random()*16777215).toString(16); app/widgets/randomBgColor/controllers/widget.js
  15. Stateful widgets Keeping track <Alloy> ! <Window class="container" layout="vertical"> !

    ! <Widget src="stateful" /> ! ! <Widget src="stateful" /> ! </Window> </Alloy> 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
  16. Data Binding 3 flavours <Model id="instance" src="myModel" instance="true" /> <Label

    id="a" text="{field}" /> <Label id="b" text="{singleton.field}" /> <Label id="c" text="{$.instance.field}" /> 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);
  17. 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
  18. 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 <Alloy /> index.xml
  19. 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
  20. @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
  21. Jade 2 XML Be lazy like David Bankier <Alloy> <Window

    class="container"> <Label id="label" onClick="doClick">Hello, World</Label> </Window> </Alloy> 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
  22. XML 2 TSS Be lazy like David Bankier <Alloy> <Window

    class="container"> <Label id="label" onClick="doClick">Hello, World</Label> </Window> </Alloy> Source: https://github.com/dbankier/xml2tss ".container": {} ".#label": {} xml2tss index.xml ../styles/index.tss • Will update existing TSS • Integrated in Alloy 1.2
  23. 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
  24. 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
  25. Wrapping Ti.UI.* components .. using widgets or CommonJS modules <ImageView

    image="http://my.im/age.png" /> Before <Widget src="ImageView" image="http://my.im/age.png" /> After: <ImageView module="My.UI" image="http://my.im/age.png" /> Coming in Alloy 1.2: • Pass on or intercept common parameters, functions and events /app/widgets/ImageView/controllers/widget.js require('My.UI').createImageView() <ImageView ns="require('My.UI')" image="http://my.im/age.png" /> Simple, durable workaround:
  26. 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)
  27. 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
  28. 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') <Widget src="ImageView" image="http://my.im/age.png" onClick="doClick" />
  29. 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