iOS/Android両対応時でNavigationWindowを使いつつAlloy Data Bindingも使う（Jade版とxp.ui）

Titanium/Alloyでマルチプラットフォーム向けにアプリを作る際、以下のような問題があります。

> Windowの中身を二度書くのがいやなのと、中身を共通化してrequireしようとするとdataCollectionがちゃんと動かないんですよねえ。  
> - [Titanium - NavigationWindowでもAlloyのCollectionを使いたいしAndroidとコードを共有したい - Qiita](http://qiita.com/yagi_/items/65c8254cb2e33261d1f6)

iPhoneでナビゲーションバーを使うにはNavigationWindowが必要ですが、Androidにはないコンポーネントなので、ビューを作るのに一工夫必要になってしまうのですが、そうするとデータバインディングと相性が悪いらしいという悩ましい問題です。

上の記事では、コントローラ内でiOSかAndroidを判断して、Androidの場合はWindowをそのまま使い、iOSの場合はNavigationWindowを生成してWindowを挿入するような形で対応する方法を紹介されていました。

正攻法で行くなら、冗長になってしまいますが、上記記事のコメントにあるようにiOSとAndroidそれぞれにWindowを書く形、

```xml
<Alloy>
  <Collection src="entry" />
  <NavigatonWindow platfrom="ios">
  </NavigationWindow>
  <Window platfrom="android">
  </Window>
</Alloy>
```

もしくは `app/views/` 以下にプラットフォームごとのフォルダを用意して、それぞれのViewを実装する形でしょうか。

データバインディングを使わないのであれば、 `require` を使ってわかりやすく共通化できるのですが。

参考: [iOSとAndroidのナビゲーションサンプル | garicchi.com](http://garicchi.com/?p=18879)

そこで、別のアプローチとして、私が普段Titanium開発をする際に使っているViewをJadeで書く（＆TSSをStylusで書く）方式での解決方法を紹介したいと思います。

参考: [Titanium Alloy with StylusでFontを設定 - Umi Uyuraのブログ](http://umi-uyura.hatenablog.com/entry/2013/12/18/011407)


XP.UI.js（Jadeを使わない方法）
------------------------------

と言いつつ、先にJadeを使わない場合の解決のひとつを紹介。

Titanium/Alloy向けの便利 [FokkeZB/UTiL](https://github.com/FokkeZB/UTiL) に含まれている [xp.ui.js](https://github.com/FokkeZB/UTiL/blob/master/app/lib/xp.ui.js) を使う方法です。

詳細は [リポジトリのドキュメント](https://github.com/FokkeZB/UTiL/blob/master/docs/xp.ui.md) に詳しく書かれていますが、iOS以外のプラットフォーム用にダミーのNavigationWindowを提供しているので、以下のように `module="xp.ui"` を設定することで、iOS/Android両方に対応できます。

```xml
<Alloy>
  <NavigationWindow module="xp.ui">
    <Window>
      <Label>Hello World</Label>
    </Window>
  </NavigationWindow>
</Alloy>
```

先日Alloy Modelの勉強していたときに使ってみたので、データバインディングと組み合わせも問題なさそうです。

[Alloy Modelの学習 - Umi Uyuraのブログ](http://umi-uyura.hatenablog.com/entry/2016/02/17/235808)


Jade版
------

Jadeでviewを拡張する方法としては、 `include` `extend` `mixin` などがありますが、このうち `mixin` を使うことにしました。

[Mixins - Jade](http://jade-lang.com/reference/mixins/)

`mixin` は、再利用可能なコードブロックを作って、任意の箇所で何度も生成できる機能です。関数のように引数を取ることもできます。

それを利用して、まずは `mixin` で基本となる画面をWindowベースで作りました。引数でプラットフォームの指定を受け取るようにしたので、生成される `<Window>` タグは各プラットフォーム専用になります。

それを、iOS用のNavigationWindow以下と、Android用にAlloyタグ配下の2箇所で呼び出すようにしました。

*app/views/index.jade*

```jade
mixin mainWindow(platform)
  Window.container(platform=platform, title="Alloy Project Demo", onOpen="doOpen", onClose="doClose")
    ListView(onItemclick="clickItem")
      ListSection(dataCollection="member")
        ListItem(title="{name}", itemId="{id}")/

Alloy
  Collection(src="member")/
  NavigationWindow(platform="ios")
    +mainWindow("ios")
  +mainWindow("android")
```

これをJadeコンパイルすると、以下のようなView XMLが生成されます。

*app/views/index.xml*

```xml
<Alloy>
  <Collection src="member"/>
  <NavigationWindow platform="ios">
    <Window platform="ios" title="Alloy Project Demo" onOpen="doOpen" onClose="doClose" class="container">
      <ListView onItemclick="clickItem">
        <ListSection dataCollection="member">
          <ListItem title="{name}" itemId="{id}"/>
        </ListSection>
      </ListView>
    </Window>
  </NavigationWindow>
  <Window platform="android" title="Alloy Project Demo" onOpen="doOpen" onClose="doClose" class="container">
    <ListView onItemclick="clickItem">
      <ListSection dataCollection="member">
        <ListItem title="{name}" itemId="{id}"/>
      </ListSection>
    </ListView>
  </Window>
</Alloy>
```

冒頭に正攻法として紹介した、iOSとAndroidそれぞれに向けて二重にWindow以下を書いた形で出力されました。当然データバインディングも問題なく動きます。


まとめ
------

Jadeを使うことで、Windowの中身を1箇所で書くことができ、煩雑さは多少減らせるのではないかな、と思います。

Jade版でのサンプルをGitHubにあげてあります。

[umi-uyura/AlloyModelWithJadeNavWin](https://github.com/umi-uyura/AlloyModelWithJadeNavWin)

ちなみに、サンプルはJadeおよびStylusから生成したView XMLとTSSも含んでいるので、Jade & Stylusがない環境の人でも、alloy.jmkを削除して `(appc) ti build` を実行してもらえば動きます。