実践入門 Ember.js

第4回 ユーザのインタラクション(Controller, Component)

この記事を読むのに必要な時間:およそ 6 分

Actions

ユーザからのイベントを受け取るために,コントローラに任意のイベントハンドラを定義することができます。 この仕組みをEmber.jsではアクションと呼びます。

以下のコントローラとテンプレートを書き換えてください。

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ヘルパーは,ユーザがDOMをクリックした際Controllerに対して発火するアクション名を指定します。 アクションがControllerでハンドルされない場合はRouteに発火します。

ちなみに,きっかけとなるDOMのイベントを変更したい場合はonオプションを利用し,アクションが発火するオブジェクトを変更したい場合はtargetオプションを利用します。

{{action "someEvent" on="mouseMove"}}
{{action "someEvent" target=otherObject}}

ここまでで,⁠もっと見る」が機能するようになりました。 しかしながら,一度「もっと見る」をクリックすると,他の記事を表示しても全文が表示されるようになってしまいます。

なぜこのような動きをするかというと,Controllerは一度生成されるとそれ以降は紐づくmodelが差し替わる形で再利用されるからです。 画面遷移しても状態を記憶しておきたい場合は便利ですが,今回の例では画面遷移で情報は初期化することにしましょう。

コントローラの初期化

画面を遷移する際,RouteがControllerとmodelを紐付ける部分を差し替えたり独自処理を付け加えることができます。 その際,利用できるのがRoutesetupControllerメソッドです。

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);

    // ここで独自の処理を行います
  }
});

では,Routeを次の内容で書き換えてください。

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.set('isExpanded', false);としてコントローラのプロパティを初期化している部分です。

これにより,画面を切り替えるたびに記事の本文が省略して表示されるようになりました。

さて,今は記事の本文だけを省略して表示しましたが,もし他の画面でも省略して表示したい項目が出てきた場合はどうすればよいでしょうか? それぞれの画面を担当するコントローラとテンプレートに同じような処理を実装していくことはできそうですが,同じような処理を何度も書くのはできれば避けたいところです。

そんなときは,コントローラでの状態管理をやめて「省略して表示する機能を持った部品」を用意することにしましょう。 これを実現するためにEmber.jsにはComponentと呼ばれる機能があります。

著者プロフィール

佐藤竜之介(さとうりゅうのすけ)

株式会社えにしテック所属。JavaScriptとRubyが好きなウェブ系プログラマ。オープンソースソフトウェアに強い関心がありEmber.jsにも数多くのパッチを送っている。

ブログ:http://tricknotes.hateblo.jp/
Twitter:@tricknotes

コメント

コメントの記入