前回は画面遷移について解説しました。 今回はデータを画面に表示するところから一歩進んで、 ユーザのインタラクションを受け取って画面を更新する方法を解説します。
今回は次のEmber.
『記事が長い場合は途中から省略して
前準備
本稿の対象バージョンはこちらです。
前回の記事と本稿執筆時点でのEmber.
ファイルの作成が完了したら、
App = Ember.Application.create();
App.Router.map(function() {
this.resource('posts', {path: '/'}, function() {
this.route('show', {path: '/posts/:post_id'});
});
});
App.PostsRoute = Ember.Route.extend({
model: function() {
return [{
id: 1,
title: 'Ember.js 公式サイトの歩き方',
body: 'Ember.js の公式サイト(http://emberjs.com/)では、まずトップページのサンプルを動かしてみるとよいでしょう。Ember.js でどんなことができるのかがざっくりわかります。'
}, {
id: 2,
title: 'Ember.jsのディスカッションフォーラム',
body: 'Ember.js についての疑問・質問・新しい提案など、Ember.js に関することが常に議論されています。 http://discuss.emberjs.com/'
}];
}
});
App.PostsShowRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('posts').filter(function(post) {
return post.id === Number(params.post_id);
})[0];
}
});
<script type="text/x-handlebars" data-template-name="posts/index">
<ul>
{{#each post in model}}
<li>{{link-to post.title "posts.show" post}}</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="posts/show">
<h2>{{model.title}}</h2>
<pre>
{{model.body}}
</pre>
{{link-to "戻る" "posts.index"}}
</script>
さて、
ではここから、
Controller
記事の詳細画面では
これだけではイメージしづらいので、
Route
が画面を描画する際、model
と一緒にコントローラも準備されます。 そしてコントローラのmodel
プロパティにRoute
で解決されたmodel
が設定され、 その状態でコントローラがテンプレートに描画されます。 テンプレートのコンテキストは、
テンプレートではコントローラのプロパティにアクセスできるため、model
をつけてプロパティを参照していました。
<h2>{{model.title}}</h2>
<pre>
{{model.body}}
</pre>
つまり、
さっそく次のコードを追記して確認してみましょう。
App.PostsController = Ember.Controller.extend({
pageTitle: 'Ember.js 関連の記事'
});
<script type="text/x-handlebars" data-template-name="posts">
<h1>{{pageTitle}}</h1>
{{outlet}}
</script>
コントローラのプロパティであるpageTitle
が画面に表示されていますね。
ちなみに、Route
のcontrollerName
を設定することで変更可能です。
App.SomeRoute = Ember.Route.extend({
controllerName: 'other'
});
この例の場合、App.
のコントローラとしてApp.
が設定されます。
Handlebars Helpers
では
次のコントローラを作成してください。
App.PostsShowController = Ember.Controller.extend({
isExpanded: false
});
テンプレートを書き換えてください。
<script type="text/x-handlebars" data-template-name="posts/show">
<h2>{{model.title}}</h2>
<pre>
{{#if isExpanded}}
{{model.body}}
{{else}}
{{truncate model.body length=20}}
{{/if}}
</pre>
{{link-to "戻る" "posts.index"}}
</script>
{{truncate model.
の部分は独自に定義したヘルパーを利用しています。 そのため、
Ember.Handlebars.helper('truncate', function(value, options) {
var length = options.hash.length;
if (value.length > length) {
return value.slice(0, length) + '...';
} else {
return value;
}
});
ヘルパーの定義
Ember.
で定義したヘルパーはテンプレートで利用できます。 ヘルパーの書式は次の通りです。
{{ヘルパー名 (引数1 引数2 ...) (オプション)}}
- ヘルパー名
Ember.
でヘルパーを定義する際に指定したヘルパー名です。Handlebars. helper - 引数
ヘルパー関数の引数です。
- オプション
key=value
の形で指定します、必要に応じていくつでも設定できます。ヘルパー関数では、 一番最後の引数の hash
プロパティから取得できます。
さて、
Actions
ユーザからのイベントを受け取るために、
以下のコントローラとテンプレートを書き換えてください。
App.PostsShowController = Ember.Controller.extend({
isExpanded: false,
actions: {
expand: function() {
this.set('isExpanded', true);
}
}
});
<script type="text/x-handlebars" data-template-name="posts/show">
<h2>{{model.title}}</h2>
<pre>
{{#if isExpanded}}
{{model.body}}
{{else}}
{{truncate model.body length=20}} <a href="#" {{action "expand"}}>もっと見る</a>
{{/if}}
</pre>
{{link-to "戻る" "posts.index"}}
</script>
テンプレートで{{action "expand"}}
と記述している点がポイントです。 このaction
ヘルパーは、Controller
に対して発火するアクション名を指定します。 アクションがController
でハンドルされない場合はRoute
に発火します。
ちなみに、on
オプションを利用し、target
オプションを利用します。
{{action "someEvent" on="mouseMove"}}
{{action "someEvent" target=otherObject}}
ここまでで、
なぜこのような動きをするかというと、
コントローラの初期化
画面を遷移する際、Route
のsetupController
メソッドです。
setupController
のデフォルトの実装は次のようになっています。
App.SomeRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('model', model);
}
});
このsetupController
を上書きすることで、
App.SomeRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('model', model);
// ここで独自の処理を行います
}
});
では、
App.PostsShowRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('posts').filter(function(post) {
return post.id === Number(params.post_id);
})[0];
},
setupController: function(controller, model) {
controller.set('model', model);
controller.set('isExpanded', false);
}
});
ポイントはsetupController
メソッドを定義して、controller.
としてコントローラのプロパティを初期化している部分です。
これにより、
さて、
そんなときは、
Component
Componentは画面上のひとまとまりの部品をカプセル化したものです。
例えば、
App.HelloWorldComponent = Ember.Component.extend({
message: 'Hello World'
});
<script type="text/x-handlebars" data-template-name="components/hello-world">
<b>{{message}} !</b>
</script>
ポイントは次のとおりです。
- クラスとテンプレートをセットで定義します。
Component名は、
クラス名から Component
を削除し、単語の区切りを -
(ハイフン) で表現したものになります。 テンプレート名は
components/
+Component名です。クラス名 Component名 テンプレート名 HelloWorldComponent
hello-world
components/
hello-world また、
このテンプレートのコンテキストはComponent自身です。 Componentを使う際はテンプレートの任意の場所でComponent名を記述します。
He said, {{hello-world}}
Componentを使う際、
必要に応じてComponentのプロパティを設定できます。 He said, {{hello-world message="This is awesome"}}
描画されたHTMLは
div
タグで囲まれます。他のタグに変更する場合、
tagName
プロパティを上書きします。{{hello-world tagName="span"}}
また、
タグを生成したくない場合は tagName
に空文字を与えます。{{hello-world tagName=""}}
Component名は必ず2単語以上で
-
(ハイフン) を含む必要があります。 これは、
将来的にはWeb Componentsとの統合が視野に入れられているためです (Web Componentsはタグ名に -
(ハイフン) を含む必要があります)。 Ember.
js 1. 11. 0からは、 Componentを利用する際は {{hello-world}}
ではなく、<hello-world>
と記述できるようになる予定です(参照:The Road to Ember 2. 0 RFC #15 )。テンプレートのみで十分な場合、
クラス定義は省略可能です。
今回はこのComponentを使って、
App.TruncateTextComponent = Ember.Component.extend({
text: null,
length: 20,
isExpanded: false,
expandText: 'もっと見る',
actions: {
expand: function() {
this.set('isExpanded', true);
}
}
});
ComponentはControllerと同様、
次はComponentのテンプレートを作成します。
<script type="text/x-handlebars" data-template-name="components/truncate-text">
{{#if isExpanded}}
{{text}}
{{else}}
{{truncate text length=length}} <a href="#" {{action "expand"}}>{{expandText}}</a>
{{/if}}
</script>
これでComponentを用意できました。 では、
記事詳細のテンプレートを以下のように書き換えてください。
<script type="text/x-handlebars" data-template-name="posts/show">
<h2>{{model.title}}</h2>
<pre>
{{truncate-text text=model.body}}
</pre>
{{link-to "戻る" "posts.index"}}
</script>
Componentは画面が切り替わると破棄されるため、Route
のsetupController
でコントローラの初期化をしている部分を削除しましょう。
App.PostsShowRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('posts').filter(function(post) {
return post.id === Number(params.post_id);
})[0];
}
});
そうすると、App.
も定義する必要ななくなるので削除します。
これで、
まとめ
今回はユーザのインタラクションを受け取る方法を解説しました。
Controller、
次回はEmber.