本文書はAlloy Controllersの日本語訳です。
本トピックでは、 Controller 及び、 Model 以外の JavaScript のコードをどのように書くかについて説明します。Alloy の Controller は UI オブジェクトを操作するために Titanium SDK API を直接呼び出し、 UI 以外の API も呼び出すので、ある程度、従来の Titanium 開発に関する知識が必要になってきます。詳細は Titanium API Guides を参照してください。
Alloy では、 Controller は UI を操作したり Model とやり取りするアプリケーションロジックを含みます。以下のコードは view (index.xml) に紐付いたプレゼンテーション層のロジック (index.js) です。
app/controllers/index.js
function doClick(e) {
alert($.label.text);
}
$.index.open();
app/views/index.xml
<Alloy>
<Window class="container">
<Label id="label" onClick="doClick">Hello, World</Label>
</Window>
</Alloy>
View 内の ID 属性を持つ UI 要素は、全て自動的に定義され、 $ という特別な変数が前についたプロパティで Controller から取得することができます。$ は Controller への参照です。たとえば、 Controller 内の
外部の Controller や View にアクセスするには、それぞれ Alloy.createController と Controller.getView メソッドを利用してください。詳細は Alloy API documentation を参照してください。
最上位の UI オブジェクトに ID が定義されていない場合、$. に Controller の名前をつけて参照してください。View の Window オブジェクトは ID を持っていないので、Controller は View から最上位の UI オブジェクトを取得するのに $.index を使います。しかし、たとえば <Window id='window'>
というように id 属性が定義されていた場合、Controller は Window オブジェクトにアクセスするのに
Controller は base (parent) Controller をひもづけることで、 exports.baseController = 'baseControllerName' というように、他の Controller を継承することができます。 CommonJS model と同様、 Controller は継承元の Controller の exported な関数をすべて継承します。これらの関数は上書きすることも可能です。
たとえば、 animal の View と Controller は speak メソッドつきで label オブジェクトを定義しています。
app/controllers/animal.js
exports.speak = function() {
alert("Yelp!");
};
app/views/animal.xml
<Alloy>
<Label id="animalLabel">Animal</Label>
</Alloy>
そして、下記のコードは animal の View, Controller を継承して speak メソッドと label のテキスト属性を上書きし、 dog Controller 用にカスタマイズしています。
app/controllers/dog.js
exports.baseController = "animal";
$.animalLabel.text = "Dog";
exports.speak = function() {
alert("Bark!");
};
Alloy はコンパイラへの指示命令のように振る舞う特別な変数のセットを持っています。これらのコンパイラ定数はコードの生成とコンパイル時の最適化に利用され、使われないコードは全て削除されます。
Controller で利用するために Alloy で定義された定数の一覧は下記のとおりです。
- OS_ANDROID : 現在のコンパイラのターゲットが Android の場合 true です。
- OS_BLACKBERRY: 現在のコンパイラのターゲットが BlackBerry の場合 true です。
- OS_IOS : 現在のコンパイラのターゲットが iOS の場合 true です。
- OS_MOBILEWEB : 現在のコンパイラのターゲットが Mobile Web の場合 true です。
- OS_TIZEN : 現在のコンパイラのターゲットが Tizen の場合 true です。
- ENV_DEV : 現在のコンパイラのターゲットが(シミュレータまたはエミュレータで動作する)開発用ビルドの場合 true です。
- ENV_TEST : 現在のコンパイラのターゲットがデバイスのテスト用のビルドの場合 true です。
- ENV_PRODUCTION : 現在のコンパイラのターゲットが(パッケージのインストール後に動作する)本番用のビルドの場合 true です。
たとえば iOS のデバイスはバックボタンを持たないため、アプリケーションは window Controller に対して条件付きのコードを追加する場合があります。
if (OS_IOS)
{
var closeButton = Ti.UI.createButton({
title: 'Close',
style: Ti.UI.iPhone.SystemButtonStyle.PLAIN
});
closeButton.addEventListener('click', function(){
$.window.close();
});
$.window.leftNavButton = closeButton;
}
外部の Controller を初期化する際にカスタマイズ用の引数を渡すことができます。たとえば、以下のようになります。
var controller = Alloy.createController('controller', {args1: 'foo'})
外部の Controller において、 arguments[0] は引数を受けとるための特別な変数です。たとえば、 TalbeView オブジェクトに複数の TableViewRow オブジェクトを追加したい場合を考えてみてください。
TableViewRow オブジェクト('row')において、 View はそのオブジェクトだけを保持し、 Controller は引数を処理するためのった数行のコードからなります。
app/views/row.xml
<Alloy>
<TableViewRow id="rowView"/>
</Alloy>
app/controllers/row.js
var args = arguments[0] || {};
$.rowView.title = args.title || '';
$.rowView.url = args.url || '';
別の Controller では TableView オブジェクト('tableView')を保持し、データ配列を繰り返し処理で、新しい 'row' オブジェクトを生成して 'tableView' に追加する処理が書かれています。
app/controllers/index.js
var data[];
for (var i=0; i<source.length; i++) {
var arg = {
title: source[i].postTitle,
url: source[i].postLink
};
var row = Alloy.createController('row', arg).getView();
data.push(row);
}
$.tableView.setData(data);
上記の例に見られるように、 Controller は 'row' Controller に異なる引数を渡し、 'row' の unique なインスタンスを生成しています。
Controller は Alloy.Globals namespace を利用してグローバルな変数を格納し、アクセスすることができます。たとえば親 Window のインスタンスを Globals に格納しておいて、別の Window からその Window にアクセスすることができます。
親 Window を格納する:
$.index.open();
Alloy.Globals.parent = $.index;
別の Controller からその親 Window にアクセスする:
var parent = Alloy.Globals.parent;
parent.close();
その他の Controller でない JavaScript のコードもグローバル変数にアクセスすることができますが、 Alloy module を require する必要があります。詳しくは下記の Alloy を拡張する を参照してください。
初期化ファイル app/alloy.js はアプリケーションのライフサイクルの始めの方でコードを実行するのに使います。このファイルの内容は始めに起動される index.js Controller が実行される直前に実行されるので、UI コンポーネントが読み込まれる前にコードを実行したり、実行される前に Alloy の組み込み関数を上書きすることができます。このファイルのコードは Alloy namespace にアクセスすることもできます。
たとえば、デフォルトの isTablet メソッドは iPad や、large または extra large グループの Android デバイス、縦横いずれかが 400 dp 以上の Mobile Web Application に対して true を返しますが、この振る舞いを上書きして、 alloy.js に以下のようなコードを追加することができます。
Alloy.isTablet = function(){
return !(Math.min(Ti.Platform.displayCaps.platformHeight, Ti.Platform.displayCaps.platformWidth) < 600);
}
Controller のコードとしては適さない JavaScript のコードがあるかもしれません。関連する View がなかったり、再利用性を高めるために MVC フレームワークから分離したかったり。その場合、 Alloy プロジェクトの app ディレクトリに lib というフォルダを作成してください。そして CommonJS モジュールや、 CommonJS の書式に従った JavaScript のコードを lib フォルダに入れてください。これらのファイルは Alloy プロジェクトのコンパイル時に Resources フォルダにコピーされます。
このライブラリや CommonJS モジュールを利用するには、ライブラリ名やモジュール名を 'app/lib' のパスと '.js' 拡張子抜きで require してください。
var lib = require('library_name');
lib.foo();
Titanium と Alloy は Node.js のコンセプトである "folders as modules" をサポートしていないので、フォルダ名を require しても自動的にフォルダ内の index.js や index.json を読み込まないし、メインの entry point を指定するために package.json ファイルを使ったりしません。ライブラリへのメインの entry point となるファイルを明示的に require する必要があります。
createController や createModel といった Alloy API のメソッドや、CommonJS modules にある Underscore.js や Backbone.js 、 app/lib にある JavaScript のファイルにアクセスするには、ライブラリの中のそれらの module を読み込む必要があります。
var Alloy = require('alloy'), _ = require("alloy/underscore")._, Backbone = require("alloy/backbone");
// Alloy extended
var foo = Alloy.createController('foo').getView();
foo.open();
// Underscore extended
var even = _.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
Ti.API.info(even);
// Backbone extended
var Book = Backbone.Model.extend();
var book = new Book({title: 'Ulysses', author: 'James Joyce'});
Ti.API.info(JSON.stringify(book));
現在、これらのモジュールは自動的に Global スコープで使えるようになっていて、モジュールを読み込まなくてもこれらの API を使えますが、 require メソッドでこれらのモジュールを読み込む前にモジュールを参照するのは非推奨で、この振る舞いは将来廃止される可能性があります。将来のリリースと互換性を持たせるには、常に require メソッドを用いてこれらのモジュールを読み込んでから、利用するようにしてください。