ControllerとModel – Ember.js入門(3)
先日、Ember.js 1.0.0 がリリースされました。RC1から約半年となりましたが、ついに正式版となります。しかし、最後の最後まで大きな変更が続いていたため、安定するのは先になるような気がします。
さて、Ember.jsでクライアントサイドMVCを学ぶシリーズの3回目は、ControllerとModelの関係についてです。一言で言えば、Ember.jsではControllerがModelをdecorateする設計となっており、View(Template)から自然にControllerやModelのプロパティにアクセスできる事が特徴です。
ModelとControllerの関係を理解する
ModelとControllerの関係を理解するために簡単なアプリケーションを作成しましょう。ここで作成するのは、ある曲を再生するためのミュージックプレイヤーです。Modelとして曲データ(タイトルとアーティスト名)を持つとし、UIでミュージックプレイヤーのボリュームを操作できることを想定します。
Templateの定義
解りやすさのため、単純なHTMLを用意します。画面はひとつであるため、Template名は省略し、デフォルトの名前(Application)を利用します。
<script type="text/x-handlebars"> <h1>Music Player</h1> <p>Title: {{title}}</p> <p>Artist: {{artist}}</p> <p>Volume: {{soundVolume}}</p> </script>
なお、今回のバージョンでは、ボリュームの変更は行えません。
Controllerを定義する
Controllerは、Ember.ObjectControllerクラスを継承して定義します。例によって命名規約に準じる必要があるため、Templateの名前に対応させなければなりません。Template名は省略しているので、ApplicationControllerとなります。
window.App = Ember.Application.create(); App.ApplicationController = Ember.ObjectController.extend({ });
なお、明示的なControllerの定義が存在しない場合、Templateに対応するデフォルトのControllerが定義されます。また、Modelが複数(配列)である場合、Ember.ObjectControllerの代わりにEmber.ArrayControllerを利用します(後日、解説します)。
Modelを定義する
ModelはRouteで定義します(参考:Routingの基本 – Ember.js入門(2))。
App.ApplicationRoute = Ember.Route.extend({ model: function() { return {title: 'Brianstorm', artist: 'Arctic Monkeys'}; } });
曲データをModelとしました。
永続化データと状態
さて、残るのはボリュームの情報です。勿論、ModelにsoundVolumeプロパティを追加すれば動作しますが、本当にその設計は妥当でしょうか?
いえ、Ember.jsでは次のようにControllerにsoundVolumeプロパティを追加します。
App.ApplicationController = Ember.ObjectController.extend({ soundVolume: 6 });
GUIアプリケーションを構築していくと、ミュージックプレイヤーの曲情報のように主として表示したいデータと、ボリュームのように補助的なデータの両方があることに気付きます。ほとんどの場合、主として表示したいデータはサーバから取得したり、ファイルから読み込むなど、永続化データです。一方、補助的なデータは、永続化することもありますが、一時的な情報で状態を表すデータです。これらを同じModelに放り込んでいくと、ModelとViewの結合度が高くなり、破綻していきます。
基本的な方針としては、永続化データはModelに、状態はControllerに定義すると綺麗な設計となります。ただし、一般的にViewからModelのプロパティを参照するために、{{model.title}}のように参照が一段階深くなってしまうのが難点です。しかし、Ember.jsではControllerがModelをデコレート(装飾:いわゆるデコレートパターン)するため、Template(View)から自然な形でModelにアクセスできます。
Template, Controller, Modelの関係
最後に、もう一度、Template(View), Controller, Modelの関係を整理しましょう。
Templateは、レンダリングする情報をControllerから取得します。Controllerに対応するプロパティがあるならば、その情報がTemplateに反映されます(soundVolume)。もし、Controllerに対応するプロパティがないならば、Modelの対応するプロパティを取得し、Templateに反映します(title, artist)。
一般的にMVCでは、ViewとControllerとModelは一方通行の参照関係を持ちます(ただし、ControllerからViewはある)。この原則を守らないと、ModelとViewの関係が強くなってしまうでしょう。変更の可能性が大きいViewに依存したModelを作らないようにしてください。
最後にコード全体を記述します。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>ControllerとModel - Ember.js</title> </head> <body> <script type="text/x-handlebars"> <h1>Music Player</h1> <p>Title: {{title}}</p> <p>Artist: {{artist}}</p> <p>Volume: {{soundVolume}}</p> </script> <script type="text/javascript" src="js/libs/jquery-1.9.1.js"></script> <script type="text/javascript" src="js/libs/handlebars-1.0.0.js"></script> <script type="text/javascript" src="js/libs/ember-1.0.0.js"></script> <script type="text/javascript"> window.App = Ember.Application.create(); App.ApplicationController = Ember.ObjectController.extend({ soundVolume: 6 }); App.ApplicationRoute = Ember.Route.extend({ model: function() { return {title: 'Brianstorm', artist: 'Arctic Monkeys'}; } }); </script> </body> </html>