Play framework 2.0でjavascriptRouterを使ってみる

2011.10.18

javascriptRouterとは?

先日の Play2.0のコミットを見ていたところ、「javascriptRouter」という聞きなれない機能のコミットがあったので確認してみました。
これはPlay2.0でajaxを使用して通信する際、routesで定義したルートとjavascript上での定義をタイプセーフにマッピングする仕組みのようです。
Scala Templateとjavascriptを組み合わせて使用することで、javascriptからPlayのコントローラーのメソッドを直接呼び出しているように使うことができます。
さらに実行時でなく画面アクセス時にurlが正しくマッピングされているかどうかもわかります。

注意:
この記事ではまだαリリース段階の、しかもHEADを使用して試しています。
HEADはほぼ毎日のように更新されており、思わぬ不具合が生じるかもしれませんので、ご了承ください。

ソースのHEADを取得してセットアップ

まだgithubからソースを取得していない人は、play2.0のソースを取得しましょう。

$ git clone https://github.com/playframework/Play20.git

すでにソースを取得している人は、最新の状態に更新してください。

$ git pull

これで現状のソースを取得できます。
ソースを取得したらビルドしましょう。

$ cd Play20/framework
$ ./build
> build-repository

これで準備ができました。

サンプルアプリケーション作成

Play20/samplesの下に、jsrouterという名前のディレクトリを作成しましょう。

$ cd Play20/samples
$ mkdir jsrouter
$ cd jsrouter
$ ../../play new

これでプロジェクトが作成されます。

まずはcontrollerをScalaで作成します。jsrouter/app/controllers.scalaファイルを下記の内容で作成してください。
また、デフォルトで生成されているjsrouter/app/controller/Application.javaファイルは削除してしまいましょう。

package controllers
 
import play.api.mvc._
import play.api.mvc.Results._
 
object Application extends Controller {

    def index = Action {
        Ok(views.html.index())
    }
    
    
    def plus(num1:String,num2:String) = Action {
        
        val resultInt =  num1.toInt  +  num2.toInt
        Ok(views.html.result(resultInt))
    }

    def minus(num1:String,num2:String) = Action {
        val resultInt =  num1.toInt  -  num2.toInt
        Ok(views.html.result(resultInt))
    }    
}

このコントローラーでは2つのメソッドを追加しています。
plusメソッドは渡された2つのパラメータを足して結果を返し、minusメソッドは引いた結果を返します。
結果のパラメータはこのあと作成するresult.scala.htmlに送られます。

次にroutesファイルを編集して、メソッドとURLのマッピングを記述します。

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

GET     /                       controllers.Application.index
GET     /plus                   controllers.Application.plus(num1,num2)
GET     /minus                  controllers.Application.minus(num1,num2)

# Map static resources from the /public folder to the /public path
GET     /public/*file           controllers.Assets.at(path="/public", file)

/plusと/minusを追加しています。

次にjsrouter/app/virew/index.scala.htmlを編集します。下記がファイル内容です。
テキストフィールドに数値を入力し、+かーボタンを押すと、ajax関数を使用してサーバー側で計算して結果を返します。
なお、javascriptRouterはjqueryとplay用のjqueryを使用する必要があるようです。

@()
<html>
    <head>
        <title>Home</title>
        <link rel="shortcut icon" type="image/png" href="http://www.playframework.org/public/images/favicon.png">
        <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <script type="text/javascript" src="@routes.Assets.at("javascripts/jquery-1.6.4.js")"></script>
        <script type="text/javascript" src="@routes.Assets.at("javascripts/jquery-play-1.6.4.js")"></script>
	@import helper.html._
	@import controllers.routes.javascript._
        @javascriptRouter("jsRoutes")(
            Application.index,
            Application.plus,
            Application.minus
        )
    </head>
    <body>
	  <input type='text' id='num1'/>
	  <input type='text' id='num2'/>
	  </br>
	  <input type='button' value='+' id='plusBtn' />
	  <input type='button' value='-' id='minusBtn' />

	  <script type="text/javascript" charset="utf-8">
		var plus = function() {
		  var num1 = $('#num1').val();
		  var num2 = $('#num2').val();
		  jsRoutes.controllers.Application.plus(num1,num2).ajax({
                    success: function(data) {
                	alert(data);
                    },
                    error: function() {
                        alert("Error!")                    
                    }
                  })
		};

		var minus = function() {
		  var num1 = $('#num1').val();
		  var num2 = $('#num2').val();
		  jsRoutes.controllers.Application.minus(num1,num2).ajax({
                    success: function(data) {
                	alert(data);
                  },
                    error: function() {
                      alert("Error!")                    
                    }
                  })
                };
		
		$('#plusBtn').click(plus);
		$('#minusBtn').click(minus);
	  </script>
    </body>
</html>

下記がScala Templateを用いている部分です。

@javascriptRouter("jsRoutes")(
     Application.index,
     Application.plus,
     Application.minus
  )

「jsRoutes」という名前でApplicationオブジェクトのメソッドを登録しています。ここで登録したメソッドしか使用することはできません。

+ボタンを押されたときに実行する関数です。 登録したjsRoutesという名前のオブジェクトを使用し、サーバー側のメソッドを呼び出すようにApplication.plus関数を使用しています。
結果は非同期でうけとり、アラートで表示しています。

var plus = function() {
  var num1 = $('#num1').val();
  var num2 = $('#num2').val();
  jsRoutes.controllers.Application.plus(num1,num2).ajax({
       success: function(data) {
                  alert(data);
                },
       error: function() {
                   alert("Error!")                    
                }
       })
 };

最後にサーバー側メソッド実行結果が送られるapp/views/result.scala.htmlを作成します。
コントローラーで計算した結果を下記htmlへおくり、index.scala.htmlのajax関数のコールバックで受け取ります。

@(result:Int)
@result

※JSON等でhtmlを介さずに値を返すことができれば楽なのですが、そういった機能が見つかりませんでした

アプリケーションを起動してみます。

% ../../play
[jsrouter] $ run

テキストフィールドに数値を入力して+やーのボタンを押してみてください。
ajaxを使用してサーバー側のメソッドを実行しているのがわかります。

まとめ

今回はPlay2.0の機能であるjavascriptRouterを使って見ました。
Scala Templateとjavascriptをうまく使用して、実行前にエラーが検出できるので便利ですね。
またPlay2.0でおもしろそうな機能を見つけたらご紹介させて頂きます。
※この記事はあくまで私がgithub上のソースを見て主観的に判断しているので、ご意見ご感想のある方はぜひご指摘ください。

参考サイトなど