3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ember.jsAdvent Calendar 2012

Day 12

Ember.Routerによる、アプリケーション状態遷移/管理

Last updated at Posted at 2012-12-26

2013/1/20追記 この後のember.jsのバージョンアップにより、この記事の内容は古くなっています。本家サイトのRoutingの説明を見ることをおすすめします。ただ、現在もRouting周りの修正が頻繁に行われているため、近い将来に、さらに挙動が変更される可能性が高いことに注意してください。

Ember.jsは、Ember.Routerというアプリケーションの状態遷移を管理する機能を持っています。
Ember.Routerは、一般的なWebフレームワークにおけるrouting管理と同様に、URLと状態を紐付け、状態遷移を管理します。
今日は、Ember.Routerを使った画面遷移のサンプルを見ていきたいと思います。

まず、少し長いですがJavaScript側のコード全体を書いておきます。あとで解説しながら部分部分を見ていきます。

app.js
//// Application
var App = Ember.Application.create({
});

//// View/Controller pairs
App.ApplicationView = Ember.View.extend({
  templateName:  'application',
  classNames: ['application-view']
});

App.ApplicationController = Ember.Controller.extend({
  slogan: 'A framework for creating ambitious web applications',
  isSlogan: true
});

App.CarsView =  Ember.View.extend({
  templateName:  'cars'
});

App.CarsController =  Ember.ArrayController.extend();

App.ShoesView = Ember.View.extend({
  templateName:  'shoes'
});

App.ShoesController = Ember.ArrayController.extend();

App.SalutationView = Ember.View.extend({
  templateName:  'salutation'
});

App.SalutationController = Ember.ObjectController.extend();

App.TraversalView = Ember.View.extend({
  templateName:  'traversal'
});

App.TraversalController = Ember.ObjectController.extend();

App.HomeView = Ember.View.extend({
  template:  Ember.Handlebars.compile('<p><a {{action goHome href=true}}><em>Go Home</em></a></p>')
});

App.HomeController = Ember.ObjectController.extend();

//// 起動時処理
App.ready = function(){
  console.log("Created App namespace");
};

//// Router定義
App.Router = Ember.Router.extend({
  enableLogging:  true,
  goToCars:  Ember.Route.transitionTo('cars'),
  goToShoes:  Ember.Route.transitionTo('shoes'),
  goHome:  Ember.Route.transitionTo('index'),
  root:  Ember.Route.extend({
    index:  Ember.Route.extend({
      route:  '/',
      connectOutlets:  function(router, context){
        router.get('applicationController').connectOutlet('greeting', 'salutation',
                                                          { greeting: "My Ember App" });
        router.get('applicationController').connectOutlet('body', 'traversal'); }
    }),
    shoes:  Ember.Route.extend({
      route: '/shoes',
      enter: function ( router ){
        console.log("The shoes sub-state was entered.");
      },
      connectOutlets:  function(router, context){
        router.get('applicationController').connectOutlet('greeting', 'salutation',
                                                          { greeting: "Shoes Route" });
        router.get('applicationController').connectOutlet('body', 'shoes');
        router.get('applicationController').connectOutlet('footer', 'traversal');
        router.get('traversalController').connectOutlet('home');
      }
    }),
    cars:  Ember.Route.extend({
      route: '/cars',
      enter: function ( router ){
        console.log("The cars sub-state was entered.");
      },
      connectOutlets:  function(router, context){
        router.get('applicationController').connectOutlet('greeting', 'salutation',
                                                          { greeting: "Cars Route" });
        router.get('applicationController').connectOutlet('body', 'cars');
        router.get('applicationController').connectOutlet('footer', 'traversal');
        router.get('traversalController').connectOutlet('home');
      }
    })
  })
});

App.initialize();

このコードに対応するテンプレートは以下になります。
Routerで定義された状態ごとに、表示用のテンプレートを用意しています。

  <script type="text/x-handlebars" data-template-name="application">
    {{outlet greeting}}
    <p {{bindAttr class="isSlogan"}} >Ember is: {{slogan}}</p>
    {{outlet body}}
    {{outlet footer}}
  </script>
  <script type="text/x-handlebars" data-template-name="cars">
    <hr/>
    <h1>Cars</h1>
    <p>Here in my car / I feel safest of all</p>
  </script>
  <script type="text/x-handlebars" data-template-name="shoes">
    <hr/>
    <h1>Shoes</h1>
    <p><a href="http://youtu.be/v_Yx0X-eHn8">N&uuml; Shooz?</p>
  </script>
  <script type="text/x-handlebars" data-template-name="salutation">
    <h1>{{greeting}}</h1>
  </script>
  <script type="text/x-handlebars" data-template-name="traversal">
    <hr/>
    <p><a {{action goToCars href=true}}>Go To Cars</a></p>
    <p><a {{action goToShoes href=true}}>Go To Shoes</a></p>
    {{outlet}}
  </script>

ではコードを見て行きましょう。まず簡単なところから。

var App = Ember.Application.create({

いつもと同様、ネームスペースのためにアプリケーションオブジェクトを作成しています。routing制御のために、アプリケーションオブジェクトが必要になります。

//// View/Controller pairs
App.ApplicationView = Ember.View.extend({

...

この行以降は、いつもと同様にView, Controllerを定義しています。画面遷移ごとに、ViewとControllerを定義しているのでかなりの量になっています。
名前には規約があり、後で出てくるRouter定義の中で使用するconnectOutletの第二引数に渡される名前とXxxView, XxxControllerの Xxx の部分が一致している必要があります。

次はちょっと飛びますが、最後の行です。

App.initialize();

アプリケーションの初期化処理を行います。initialize()メソッドが行う処理は以下のとおりです。

  • App.Routerのインスタンスを作成し、App.routerにセットします。
  • 定義されているView, Contollerのクラスすべてのインスタンスを作成し、App.routerのプロパティにセットします。
  • Viewに対してcontrollerプロパティに、対応するControllerクラスのインスタンスをセットする。

次はいよいよ本題であるRouter定義を見ていきます。

App.Router = Ember.Router.extend({
  root:  Ember.Route.extend({
    index:  Ember.Route.extend({
    }),
    shoes:  Ember.Route.extend({
    }),
    cars:  Ember.Route.extend({
    }),
  })
})

大きな構造として、App.Routerは上のような構造になります。

rootメンバは、Routerに必ず定義しないといけません。これがないとエラーになります。
index, shoes, carsは、それぞれURLに割り当てられ、アプリケーションの状態を表現します。Ember.jsアプリケーションは、これらのRouterを遷移して、アプリケーションの状態を表現します。

  goToCars:  Ember.Route.transitionTo('cars'),
  goToShoes:  Ember.Route.transitionTo('shoes'),
  goHome:  Ember.Route.transitionTo('index'),

これらの定義は、Viewテンプレートからactionの引数に渡されて使用されます。それぞれcars, shoes, indexへの状態遷移を行います。
使用例を示します。

  <p><a {{action goToCars href=true}}>Go To Cars</a>

次に、各Router定義の中身を見ていきます。

    shoes:  Ember.Route.extend({
      route: '/shoes',
      enter: function ( router ){
        console.log("The shoes sub-state was entered.");
      },
      connectOutlets:  function(router, context){
        router.get('applicationController').connectOutlet('greeting', 'salutation',
                                                          { greeting: "Shoes Route" });
        router.get('applicationController').connectOutlet('body', 'shoes');
        router.get('applicationController').connectOutlet('footer', 'traversal');
        router.get('traversalController').connectOutlet('home');
      }
    }),

routeメンバは、マッピングされるURLを指定しています。
enterメンバに指定されたメソッドは、このRouterの状態に遷移したタイミングで呼び出されます。
connectOutletsというメンバが、状態ごとの画面遷移の肝になります。Viewテンプレートに{{outlet}}ヘルパを記述すると、状態ごとに表示するView/Controllerを切り替えることができます。

        router.get('applicationController').connectOutlet('body', 'shoes');

connectOutletsの中で上のように定義されているとします。第一引数がoutlet名。

  <script type="text/x-handlebars" data-template-name="application">
    {{outlet body}}
  </script>

そしてテンプレートにこのように記述されていると、shoes状態に遷移した時、{{outlet body}}の箇所に、connectOutlet第二引数、'shoes'に対応するView/Controllerである、ShoesController/ShoesViewの内容が挿入されます。
{{outlet}}の引数がないなら、connectOutletがView/Controllerのプリフィクスを1つだけ引数をとります。以下の例だと、HomeView/HomeControllerを挿入します。

        router.get('traversalController').connectOutlet('home');

以上がEmber.Routerの基本的な使い方になります。
以下のページで、実際に動いているサンプルを確認できます。

なかなか直感的に状態遷移を記述できると感じました。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?