ControllerとModel – Ember.js入門(3)

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

先日、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>

参考