KnockoutJsとHaxeでアプリケーションの構造を考える

去年のAdventCarenderで[KnockoutHxを使って小規模SPA](http://qiita.com/k-motoyan/items/a9ff4d223ea659bf9703)という記事を書いたのですが、
内容があまりになさすぎて貴重なAdventCarenderの１枠を潰してしまったことをとても後悔していました。
（多分、Knockout界隈の人かなり怒ってたと思う…）

リベンジとか供養とか謝罪の意とか諸々を兼ねて、この投稿を書きました。。
この投稿では、サンプルアプリケーションを題材にKnockoutJsとHaxeを利用したフロントエンドアプリケーションの設計について説明します。

サンプルアプリケーションはHaxeで書かれているのでコードがクラス単位で詳細に分割されています。
knockoutjsはRequire.jsを利用した外部モジュールの読み込みを行うような開発が出来るので（[参考](http://kojs.sukobuto.com/docs/amd-loading)）jsの場合は適宜読みかえれば同じような構造で開発出来るはずです。

# サンプルアプリの構成

ソースコードは[Github](https://github.com/k-motoyan/knockout-haxe-sample-application)から取得出来ます。
サンプルサイトは[コチラ](https://k-motoyan.github.io/knockout-haxe-sample/)で確認出来ます、ちょっとリッチなテキストサイトをイメージしてあります。
アプリケーションのフォルダ構成は以下の様にしました。

```
knockout-haxe-sample/
  L bin/
  L src/
    L ks
      L components/
      L data/
      L enums/
      L services/
        L api
        L event
      L styles
      L templates
      L view_models
      DiContainer.hx
      Main.hx
      Service.hx
  L tests
  Makefile
  Gulpfile.js
  bower.json
  package.json
```

## components

knockout3.2から導入された機能らしい。
viewModelとtemplateを１つのカスタムタグとしてパッケージする機能で、
HTMLのタグレベルで部品の再利用が可能になる。

参考
http://tango.hatenablog.com/entry/2014/12/03/000649

サンプルアプリケーションでは、
templateとviewModelクラスを関連付けるために定義している。
データと画面構造をパッケージするものという意味で捉えていて、それ以外の仕事は一切やらない。

knockoutjsの公式サイトでは、viewModelをfunctionとして登録するように書かれているが、
サンプルアプリケーションのようにviewModelを別のクラスに分離しておくとこのような書き方では実行時にエラーになってしまう。

```js:公式サイトの方法
ko.components.register('sample', {
  viewModel: function() { /* ... */ },
  template: '<h1 data-bind="text: title"></h1>'
});
```

クラスを別に分離した場合、そのインスタンスをinstanceプロパティに指定して登録するようにする。

```hx
Knockout.components.register("sample", {
  viewModel: { instance: new ViewModel() },
  template: "<h1 data-bind='text: title'></h1>"
});
```

> viewModelに関数を登録する場合、カスタムタグに`params`属性を利用してデータを渡すことが出来る。
インスタンスを登録する場合にはこの方法が利用出来ないので、componentクラスのコンストラクタを通してデータを渡すようにするといい。

### テンプレートの取得方法

個人的に、htmlタグをコードの中にベタ書きする方法が気持ち悪く感じてしまう。
（エディタの恩恵も受け難く、そもそもhtmlを書くことが嫌だ。）

サンプルアプリケーションではHaxeのリソース埋め込み機能を利用して、コード内に埋め込んだテンプレートを取得するようにしている。

コードへの埋め込みはコンパイル時に行われる。
Htmlの拡張文法を利用する場合は、プレコンパイルのステップでHtmlにコンパイルしてから埋め込むようにするといい。

> knockoutjsではテンプレートを外部に持つ場合、Require.jsを利用して読み込む方法が用意されている。
Haxeのリソース埋め込み機能との違いは、最初に一括で読み込まれるか（Haxeの場合）、非同期に読み込まれるか（Require.jsの場合）の違いがある。

## data

データの構造を持つクラスを置く場所。
knockoutとは無関係の場所なので詳細は割愛。

## enum

Haxeで書く場合はenumが使えます。
enumがあることでJsでは文字列で管理しないといけなかったもの（オブジェクトコンテナへのアクセス、カスタムイベントなど）がしっかり定義出来るのでとてもよいです。

## style / template

styleフォルダ以下にはstyle定義を置くようにしています。
サンプルアプリケーションでは行っていませんが、Haxeのリソース機能を利用してcomponentに一緒にパッケージ出来ればいいなと考えています。

templateフォルダ以下にはviewModelに対応するviewファイルが置かれています。
このフォルダに置かれているviewはjadeで書かれていて、
一度HtmlにコンパイルしてからHaxeのコンパイル時に出力されるJs内に埋め込まれます。

## view_models

実質controllerのような役割になった。
viewとデータバインディングと、イベントのディスパッチャがこのクラスの役割になる。
役割が２つなのでこのへんの設計はもっと綺麗に出来る気がしています。

## services

特定のインターフェース毎に機能をまとめて追いておくところ。
サンプルアプリケーションではRestAPIインターフェースとイベントインターフェースを定義しました。

ここに定義したクラスは`Service`クラスを通してアクセスさせるようにしています。

# Tips

knockoutjsとHaxeを利用する上で、個人的に気をつけていること。

## viewModel(component)間のデータ連携はイベントを利用する

`$root`、`$paretn`といったviewから扱える変数があり、それをview上でイベントハンドラにバインドしてviewModle間のデータ連携をする方法もありますが、
そういった依存関係をviewの中で作ってしまうのはviewの責務を超えているように思います。

viewModle間のデータ連携はイベントのディスパッチを通して一方通行で行うようにしたほうが、システムを疎結合に保つことが出来ます。

## observerから取得する値が文字列になってしまう

`computed`でリストの合計値を計算しようとして、observerから値を取得して加算して…という処理を書くと文字列の連結が行われた結果が出力されてしまう。

obaserverから取得した値はIntへキャストしてから集計するようにする。
（Haxeだと、Intで型定義してあるのに実データは文字列になって入力されるので、一旦Dynamicで型の情報を消したあとに数値にキャストする必要がある。）

## Haxeでobserverから値を取るときはset、getメソッドを使う

Haxeでobserverへアクセスするには`observer()`といった記述は出来ません。
代わりに、`observer.get()`、`observer.set(var)`といった記述を利用します。

ちなみに、`observer.set(var)`は`observer << var`といった記述をすることも可能です。
こういった拡張文法が作れるとことがHaxeのキモいところでもあり、イケてるところでもありますね。