ちょっと話題の記事

Ember.js はじめました – Ember.js入門(1)

2013.08.29

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

こんにちは、渡辺です。テスト系のエントリーやAWS系のエントリーも書きたいのですが、なぜかクライアントMVCのエントリーを投下することになりました。

ここ数年、多くのクライアントサイドのMVCフレームワークがリリースされています。ひとつの流行ではあると思いますが、ある程度の淘汰も進み、ひとつの実装パターンとして認知されているでしょう。そこで、最近になって注目している人も多い、Ember.jsについてとりあげていきたいと思います。

Ember.jsとは?

今回とりあげるEmber.jsは、幾多のクライアントサイドMVCフレームワークのひとつです。現在も活発に開発が行われており、本稿執筆時点での最新バージョンは、1.0.0-rc.7です。RCなのでもうすぐ正式版がリリースされるという話ですが、そんな話が半年近く続いています。 一言で雰囲気を伝えるならば、「なんかRailsっぽいな」というフレームワークです。しかしながら、表面的にはRails臭がするのですが、中身はガチのMVCであり、データバインディングコンポーネントなどの機能を提供しています。

なお、MVC(ウェブMVCとは違います)を経験したことの無い人には馴染みのない概念などもありますので、そのへんも含めて連載していこうかと考えています。

概念

Ember.jsの概念となる用語を簡単に解説します。自分もまだ完全に把握しきれていないため、間違って理解している点などがあればコメントなどでご指摘ください。

Template

Templateはユーザインターフェイスとしてユーザに表示されるHTMLの定義です。Ember.jsではHandlebarsというテンプレート言語を利用してTemplateを記述します。

Templateは、Modelと関連付いており、Modelのプロパティなどをレンダリングします。例えば、ユーザModelが関連付いているTemplateはこんな記述となります。

<script type="text/x-handlebars">
  <p>Name: {{name}}</p>
  <p>Age: {{age}}</p>
</script>

これがレンダリングされると、次のようになります。

  <p>Name: Test User</p>
  <p>Age: 21</p>

user.nameのようにモデル名を記述さず、単純にプロパティ名をプレースホルダ{{}}の中に記述しています。

また、TemplateとModelはバインディングされており、Modelのプロパティを変更することによりTemplateは再レンダリングされ、Template(HTML)が変更されるとModelのプロパティが変更されます。このため、GUIアプリケーションを構築している時に記述しなければならない、変更イベントの多くを省略できます。

Router

Routerは、WebアプリケーションにおけるRouterと同様に、リクエストされたURLを対応するTemplate(およびModel, Controller)に変換する役割を持ちます。

Ember.jsではページ内のリンクを作成すると、ページ上のパスが変更されますがサーバにHTMLリクエストを行いません。内部でTemplateの切り替えを行います。

また、URLは表示しているページに対応したURLがブラウザのアドレスバーに表示されるため、URLをコピーして他のユーザに伝えたり、ブックマークして任意のページからアプリケーションを開始することも可能です。

関連エントリー

Component

ComponentはTemplateの一種ですが、再利用可能な小さな部品と考えると良いでしょう。 組み込みで提供されている部品としては、linkToがあります。このComponentは文字どおりハイパーリンクを作成するComponentであり、アプリケーションの設定などから最適なリンクを自動的に作成します。

ユーザはTemplateの一部をComponetに置き換えていくことで、コピペ地獄から解放されるはずです。ComponentはTemplateと、同じように作成することができます。

Model

Modelは、永続化データを格納するためのオブジェクトです。Template上でレンダリングされ、ユーザに情報を提供します。

ほとんどのアプリケーションで、ModelはサーバからJSON APIを使ってロードされます。そして、必要に応じてサーバに保存するためのリクエストを行うでしょう。

Ember.jsではTemplate/Model/Controllerをワンセットで組み合わせることも簡単で、そのような場合はRuby on Railsのように、ほとんどのコードを書かずに、CRUD風なGUIを作ることもできます(個人的には、そういったUIはEmber.jsを使わずにベタにHTMLを返した方が良いと思いますが…)。

関連エントリー

Route

Routeは、どのModelを表示するかをTemplateに伝えるオブジェクトです。

Routerとは異なるので注意してください。解りにくいです。

Controller

Controllerはリクエストを受け付けて処理を行う窓口…ではありません。MVCにおけるControllerは、Template(View)とModelの接着剤としての機能を持ちます。

その機能のひとつとしては、イベント処理の窓口です。これはウェブMVCとどうようにイベントハンドラを定義し、イベント発火時の処理を記述します。Modelの変更などを行えば、後は勝手にTemplateが再レンダリングされるので、非常に簡単です。

また、Controllerはアプリケーションの状態も管理します。これは、永続化が不要で、一時的に保持したいデータと考えれば良いでしょう。

関連エントリー

MVCに慣れていないと、全部を一度に理解するのは難しいかもしれません。少しずつ慣れていくとしましょう。

Hello World

とりあえず、Hello Worldです。

しかし、なにが「とりあえず」なのか、Ember.jsでは解りません。公式サイトのチュートリアルは、最初から本格的なアプリケーションになっていますので、敷居が高く感じました。

というわけで、TemplateとRouteのみを利用してみました。最後にコード全体を記述しますが、予めjquery,handlebars,ember.jsのライブラリをロードしておく必要があります。

window.App = Ember.Application.create();
App.ApplicationRoute = Ember.Route.extend({
	model: function() {
		return {name: 'Ember.js'};
	}
});

名前空間とApplicationインスタンスの作成

最初の1行は、Ember.jsのアプリケーションで使うための名前空間を作成し、初期設定を行うコードです。

window.App = Ember.Application.create();

Appには任意の名前を付け、アプリケーションで作成するクラスや変数は全てApp名前空間に属することになります。これは、グローバルの名前空間を汚染しないための、JavaScriptの一般的なテクニックです。

Ember.Applicationの作成はcreateメソッドを用います。初期設定などを追加することもできますが、ここではデフォルト設定でインスタンスを作成しました。

Routeオブジェクトの定義

Ember.jsでは様々なオブジェクトが、自動的に、命名規約に従って、作成されます。このため、コードの記述量が少なくて済むのは良いのですが、「なにが起きているか解らないと、全く何も解らない」ことになります。Railsと同じですね。

Routeはそんな自動的に作成されるオブジェクトのひとつです。

2行目のコードは、このアプリケーションでのRouteのカスタマイズを行っています。

App.ApplicationRoute = Ember.Route.extend({
  model: function() {
    return {name: 'Ember.js'};
  }
});

Hello Worldでは、複数のページを持たないアプリケーションです。このような場合は、App.ApplicationXxxといったクラスが動的に生成されます。ここでは、ModelとTemplateを関連付けるRouteを定義するため、App.ApplicationRouteを定義します。クラスは、Ember.Routeクラスを拡張したクラスです。この時、modelプロパティを指定しています。

modelプロパティはfunctionであり、単純なオブジェクトを返します。

これで、TemplateにModelが関連付けられました。

実行してみる

ブラウザでアプリケーションを実行してみると、Templateのプレースホルダnameが「Ember.js」と置き換わって表示されるでしょう。

何が起きたか?

HelloWorldでは、TemplateとModelをRouteで関連付けるというコードを記述しました。Templateは癖の無いプレースホルダで記述し、Modelは汎用的なオブジェクトでした。これらを関連付けるのがRouteオブジェクトです。

Controller(App.ApplicationController)も作られていますが、今回はデフォルトの動作しか提供していないため、再定義していません。Controllerでは、Templateからの要求に従い、対応するModelのnameプロパティをレンダリングするデータとして返しています。

さて、これでは何が嬉しいのかさっぱり解りません(笑)Ember.jsの闇機能はたくさんあるので、少しずつ紹介していこうと思います。

なお、反響が大きいほど、次のエントリーの投下が速くなると思いますので、早く続きが読みたい方はワッフルしてください。

最後にソースコード全体を示します。

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World - Ember.js</title>
</head>
<body>
	<script type="text/x-handlebars">
<h1>Hello World</h1>
<p>Hello, {{name}}</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-rc.7.js"></script>
	<script type="text/javascript">
window.App = Ember.Application.create();
App.ApplicationRoute = Ember.Route.extend({
	model: function() {
		return {name: 'Ember.js'};
	}
});
	</script>
</body>
</html>