前回はEmber.
今回からは簡単なアプリケーションを作成しながら、
今回のテーマはRoutingとTemplatesです。
なぜこの2つから解説を始めるかというと、
今回のゴールは次のようなEmber.
前準備
さて、
前回からのアップデート
前回の記事からのEmber.
ファイルの準備
まずはEmber.
jsを使うために必要なライブラリをダウンロードします。 HTMLとJavaScriptファイルを作成します。
index.
html <!DOCTYPE html> <html> <head> <script src="libs/
jquery-2. ></script> <script src="libs/1.1. min. js" handlebars-v2. ></script> <script src="libs/0.0. js" ember. ></script> <script src="app.js" js" ></script> </head> <body> </body> </html>app.
js App = Ember.Application.create();
次のディレクトリ構成で配置します。
. ├── app.
js ├── index. html └── libs ├── ember. js ├── handlebars-v. 2.0. 0.js └── jquery-2. 1.1. js
開発ツールのインストール
Ember.
- Google Chrome版 … Ember Inspector - Chrome ウェブストア
- Firefox版 … Ember Inspector :: Add-ons for Firefox
この拡張を入れると、
![画像](/assets/images/dev/serial/01/emberjs/0002/thumb/TH800_inspector-tab.png)
このタブでは、
さて、
Templates
では、
Ember.
ただ、
ではHandlebarsの記法を見ていくことにしましょう。
先ほど作成したindex.
のbody
タグの中に次のタグを追加してください。
<script type="text/x-handlebars" data-template-name="index">
<h1>{{title}}</h1>
タグ:
{{#each tag in tags}}
<span>{{tag}}</span>
{{/each}}
{{!これはコメントです}}
<p>{{body}}</p>
書いた人: {{author.name}}
<div {{bind-attr class=status}}>
{{publishedAt}} 公開
</div>
{{#if author.twitter}}
(@{{author.twitter}})
{{/if}}
<hr>
記事を修正する:
{{input value=body}}
</script>
また、app.
に次の内容を追記してください。
App.IndexRoute = Ember.Route.extend({
model: function() {
var post = {
title: '実践入門 Ember.js',
body: 'Ember.jsについて解説します。',
tags: ['Ember.js', 'JavaScript'],
status: 'newly',
publishedAt: '2014年12月23日',
author: {
name: '佐藤竜之介',
twitter: 'tricknotes'
}
};
return post;
}
});
これは、post
が参照するオブジェクトをテンプレートに渡すためのコードです
さて、
とくにCSSを入れていないのでとても簡素な見た目ですが、
<script type="text/
x-handlebars" data-template-name="index"> script
タグにtype="text/
を指定することで、x-handlebars" Ember. jsで使うためのテンプレートであることをEmber. jsに知らせています。また、 data-template-name
に指定した値がテンプレートの名前になります[4]。Ember. jsがテンプレートを探索する際はこの名前を利用します。 <h1>{{title}}</h1>
{{
から}}
で囲まれた部分にはオブジェクトのプロパティやHandlebarsのヘルパーを埋め込むことができます。ここではオブジェクトのプロパティであるtitle
を埋め込んで表示しています。{{#each tag in tags}}
...{{/each}}
値が配列の場合、
each
ヘルパーを使うと要素をひとつひとつ取り出せます。この#
と/
で範囲を指定するヘルパーをHandlebarsの用語では"Block Expressions"と呼びます。{{!これはコメントです}}
{{!
~}}
の範囲はコメントです。HTMLには描画されません。{{author.
name}} オブジェクトがネストしている場合、
.
で深い階層の値を参照できます。<div {{bind-attr class=status}}>
HTMLの属性を埋め込みたい場合は
bind-attr
ヘルパーを使います。下の例のように属性に{{
~}}
を埋め込んだ場合、エラーになってしまい動作しません。 <div class="{{status}}">
これはEmber.
jsが提供するHandlebarsでの値の自動更新の実装上の制約です。 ただ、
Ember. js 1. 10. 0から導入予定のHTMLBarsではこの制約はありませんので、 上の例のように属性を直接埋め込んでも動作します。 {{#if author.
twitter}} 値の真偽で出力を切り替えられます。通常のJavaScriptと同じく
0
、''
(空文字) が偽として評価されるほか、 空配列も偽として評価されます。 if
の他に、値が偽のときに評価される unless
もあります。そしてそのどちらもelse
が指定可能です。次の2つの例は同じように評価されます。
{{#if isPublished}} {{else}} (この記事は未公開です) {{/if}} {{#unless isPublished}} (この記事は未公開です) {{/if}}
{{#unless isPublished}} (この記事は未公開です) {{/if}}
else
は範囲を取らない( {{#else}}
~{{/else}}
とならない)ことに注意してください。 {{input value=body}}
input
タグを生成するヘルパーです。value=body
とすることで、input
タグのvalue
属性とオブジェクトのbody
プロパティを結びつけています。これによって、ユーザがテキストフィールドに値を入力することで画面に表示されている body
も追従して変更されます。このような仕組みを「データバインディング」 と呼びます。Ember. jsはデータバインディングを全面的にサポートすることで、 アプリケーションから決まりきった画面更新のロジックを排しプログラマの負担を軽減してくれます。
以上、
Routing
次は Routingを見ていきます。
RoutingはURLとアプリケーションの画面を対応付ける機能です。
例えば、
もちろんこれはユーザにとってもメリットがあります。アプリケーションの状態をURLで表現できることは、
RESTfulなWebアプリケーションを設計する際と同様、
Ember.#
からはじまるフラグメントハッシュの形で提供します#/
以下にEmber.
RoutingにはRouter
とRoute
という2つの部品が登場します。それぞれについて、
![画像](/assets/images/dev/serial/01/emberjs/0002/thumb/TH800_architecture.jpg)
ここではapp.
とindex.
を準備したばかりの状態から始めることにします。
app.js
App = Ember.Application.create();
// Router
App.Router.map(function() {
this.route('welcome', {path: '/'});
});
// Route
App.WelcomeRoute = Ember.Route.extend({
model: function() {
return {
name: 'さとう'
};
}
});
テンプレートはこちらです。index.
のbody
タグの中に記述してください。
<script type="text/x-handlebars" data-template-name="welcome">
こんにちは, {{name}} さん
</script>
では、
Router
Router
は今ブラウザで表示しようとしているURLについて、Router.
メソッドの中でthis.
の形式でRoute
とURLの対応づけを行います。
App.Router.map(function() {
this.route('welcome', {path: '/'});
});
この例では、WelcomeRoute
を/
というURLに対応付けていますRoute
を削って小文字にしたものです。今回の例だと、WelcomeRoute
に対応するroute名はwelcome
です。
もしURLとroute名が同じであれば、path
の指定を省略できます。
App.Router.map(function() {
this.route('welcome');
});
この記述だと、#/welcome
というURLでアクセスされた場合WelcomeRoute
が有効になります。
また、#/
とIndexRoute
の対応を持っています。したがって{path: '/'}
を省略した場合、IndexRoute
を自分で定義することで#/
の画面に表示するオブジェクトを用意できます
Route
Router
によって処理を引き渡されたRoute
は、
App.WelcomeRoute = Ember.Route.extend({
model: function() {
return {
name: 'さとう'
};
}
});
model
メソッドの戻り値がこのRouteに対応するモデルオブジェクトになります。Route
はモデルオブジェクトをテンプレートに渡して画面を描画します。このとき利用されるテンプレートはroute名と同じものが使われます。この例の場合は、data-template-name="welcome"
のテンプレートが使われます。
通常だとモデルオブジェクトはサーバサイドのAPIを呼び出して取得したデータを利用することが多いのですが、
デバッグオプション
さて、Route
を作成することになります。そうすると、Route
が定義されていて今どのRoute
が有効になっているのか知りたくなることでしょう。
そんなときのために、
app.
の以下のように書き換えてください:
App = Ember.Application.create();
App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_VIEW_LOOKUPS: true
});
LOG_
… Route間の遷移をログ出力しますTRANSITIONS (参考: LOGGING ROUTE CHANGES) LOG_
… 画面表示に使用するテンプレートをログ出力しますVIEW_ LOOKUPS (参考: LOG VIEW LOOKUPS)
これらのデバッグオプションを有効にしておくことで、
これで、
![画像](/assets/images/dev/serial/01/emberjs/0002/thumb/TH800_log_transitions_and_view_lookups.png)
テンプレート
<script type="text/x-handlebars" data-template-name="welcome">
こんにちは, {{name}} さん
</script>
Route
のmodel
メソッドで返したオブジェクトを表示します。これは先ほどTemplatesの項で解説した通りです。
Route間の遷移
さて、
そして、
まずはRouterとRouteを用意します。
App.Router.map(function() {
this.route('posts', {path: '/'});
this.route('post', {path: '/post/:id'});
});
var posts = [
{
id: 1,
title: 'Ember.js の世界',
body: 'Ember.js の全体像をご紹介します。'
}, {
id: 2,
title: 'Ember.js の秘密',
body: 'Ember.js のとっておきをご紹介します。'
}, {
id: 3,
title: 'Ember.js の奇妙な冒険',
body: 'Ember.js の独特な世界観をご紹介します。'
}
];
App.PostsRoute = Ember.Route.extend({
model: function() {
return posts;
}
});
App.PostRoute = Ember.Route.extend({
model: function(params) {
var found = posts.filter(function (post) {
return post.id === Number(params.id);
});
return found[0];
}
});
次はテンプレートを用意します。
<script type="text/x-handlebars" data-template-name="posts">
<h1>記事一覧</h1>
<ul>
{{#each post in model}}
<li>
{{link-to post.title "post" post}}
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="post">
<h1>{{title}}</h1>
<p>
{{body}}
</p>
{{link-to "戻る" "posts"}}
</script>
完成イメージは次の通りです。
URLが更新される様子を確認するために、
記事のタイトルをクリックして詳細を表示すると、id
が含まれていることが確認できます。これで、
これは非常に簡単な例ですが、
新登場のヘルパー
では、
link-to
link-to
はaタグを出力するヘルパーです。
これを使うと与えられたオブジェクトから生成したURLがhref
に設定され、
{{link-to "次へ" "hello" model}}
link-to
の引数は次のようになっています。
link-to (テキスト) (route名) (オブジェクト) (オプション)
- (テキスト)
生成されるタグのテキストです。
{{#link-to (route名) (オブジェクト) (オプション)}} テキスト {{/link-to}}
と書くこともできるので、
テキスト部分にHTMLを含めたい場合こちらを使う方が記述しやすいです。 - (route名)
this.
で指定したRouteの名前です。route() this.
の形でRouteを定義している場合はroute(name, {path: path}) name
の部分です。- (オブジェクト)
テンプレート中での
model
は、Routeの model
メソッドで生成されたオブジェクトを指します。link-to
にオブジェクトを渡すと、this.
で定義したroute() path
にしたがってURLが生成されます。this.
の場合、route('post', {path: '/post/:id'}) model.
がURL生成に利用されます。id - (オプション)
ここには
link-to
が生成するHTMLのためのオプションを渡すことができます(省略可能)。指定可能なオプション一覧はAPIリファレンスの"PROPERTIES"を参照してください。 例えば、
以下のような使い方ができます。 {{link-to post.title "post" post target="_blank"}}
アプリケーションのレイアウト
いくつもの画面を作成していった場合、
<script type="text/x-handlebars">
<header>
<h1>Ember.js</h1>
</header>
<aside id="sidebar">
...
</aside>
<main>
{{outlet}}
</main>
<footer>
...
</footer>
</script>
ここでのポイントは2つあります。
data-template-name
を指定していないdata-template-name
を省略すると、アプリケーションでの共通レイアウトとして設定されます [9]。 {{outlet}}
ヘルパーを使っている画面遷移を行うと、
共通のレイアウト中の {{outlet}}
に各Routeで描画されたテンプレートが差し込まれます。
この共通テンプレートを使うことで、Route
で使われるテンプレートをシンプルに保つことができます。
以上でRoute間の遷移についての解説は終わりです。
まとめ
今回はEmber.
次回はもう少し実践的な例を用いて、