Play framework 2.0 RCが出たようです13 – Bootstrapを使う

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

RC版のリリース

2月某日、以前からの予告どおり、Playframework2.0のRC版がリリースされました。
現在はRC2がダウンロード可能で、近々RC3がリリースされるものと思われます。
RCとはいえ、まだバグフィックスや機能追加が頻繁にされておりますので使用にはご注意を。

Bootstrapとは?

Bootstrapとは、twitterが提供するWebアプリケーション用CSSフレームワークです。
いくつかのファイルを読み込んで決まったスタイルを指定するだけで、見栄えの良い画面を作成することができます。
boootstrapのデモをみてみましょう。

こんな何の変哲もない画面が、

BootstrapのCSSを追加して、タグにあらかじめ定義されたクラスを適用するだけで、このような画面になります。

非常に簡単です。手軽にある程度見栄えの良い画面を作成するには十分すぎる機能をもっていますし、カスタマイズも可能です。
今回はPlayframeworkのScalaテンプレートからBootstrapをつかってみます。

PlayframeworkからBootstrapを使用するには

Playframework2.0ではデフォルトでScalaテンプレートを使用してビューを記述します。
これはいわゆるJSPみたいなもので、htmlの中にScalaのコードを埋め込むようなことができます。
普通にHTMLを記述するなら、特別なことはせずBootstrapを使用できるのですが、
helper.twitterBootstrapパッケージを使用すれば、フォームを使用するときに自動的にスタイルを適用してくれます。
ではフォームをつくってみましょう。

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.7.2
  • Playframework : HEAD
  • Java : 1.6.0_26
  • Bootstrap : 2.0

注意:
この記事ではgithubから取得したHEAD版を使用して試しています。
正式リリース前の状況のため思わぬ不具合が生じるかもしれませんので、ご了承ください。

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

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

$ git clone https://github.com/playframework/Play20.git
$ cd Play20/framework
$ ./build
> complile
> build-repository
> publish-local

すでにソースを取得している人は、最新の状態に更新してビルドします。

$ git pull
$ cd Play20/framework
$ ./cleanEverything
$ ./build
> compile
> build-repository
> publish-local

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

プロジェクト作成

Scalaベースのプロジェクトを作成し、sbtコンソールを起動します。

% play new bootstrap
% cd bootstrap
% play

eclipsifyを実行してeclipseプロジェクト化してインポートしてソースの修正をしましょう。

>[bootstrap] $ eclipsify

Bootstrapをダウンロード&コピー

まずはここからBootstrapをダウンロードして解凍します。
bootstrapプロジェクトディレクトリのpublic下にbootstrapディレクトリを作成し、そこにboostrap/css,bootstrap/img,bootstrap/jsをコピーします。

Controller修正

Applicationコントローラーを修正します。
フォーム表示関数(form)とフォームサブミット関数(submit)を作成します。
Formに関する部分は、このあたりをごらんください。

package controllers

import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._

import views._


object Application extends Controller {

  val userForm: Form[User] = Form(
    
    // Userフォームマッピング
    mapping(
      "username" -> text(minLength = 4),
      "email" -> email,
      "password" -> tuple(
        "main" -> text(minLength = 6),
        "confirm" -> text
      ).verifying(
        // パスワードの入力ルール定義
        "Passwords don't match", passwords => passwords._1 == passwords._2
      ))

    {
      // Userフォームバインド
      (username, email, passwords) => User(username, passwords._1, email) 
    } 

    {
      // Usetフォームアンバインド
      user => Some(user.username, user.email, (user.password, ""))
    }.verifying(
      "This username is not available",
      user => !Seq("admin", "guest").contains(user.username)
    ))
  
  /**
   * ユーザー登録.
   */
  def form = Action {
    Ok(views.html.index(userForm));
  }
  
  
  /**
   * ユーザー登録結果.
   */
  def submit = Action { implicit request =>
    userForm.bindFromRequest.fold(
      errors => BadRequest(views.html.index(errors)),
      user => Ok(views.html.result(user))
    )
  }

  
}

//ユーザー情報クラスを定義
case class User(
  username: String, 
  password: String,
  email: String
)

Scalaテンプレート修正

まずはapp/views/main.scala.htmlを修正します。
Bootstrapを読みこむようにしましょう。

@(title: Html, nav: String = "")(content: Html)
<!DOCTYPE html>

<html>
    <head>
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
		<link rel="stylesheet" media="screen" href="@routes.Assets.at("bootstrap/css/bootstrap.min.css")" >
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
		<script src="@routes.Assets.at("bootstrap/js/bootstrap.min.js")" type="text/javascript"></script>
    </head>
    <body>
        @content
    </body>
</html>

次はindex.scala.htmlにフォームを記述します。
バインド済みのuserForm変数を用いて、@helper.formでフォームタグ生成、エラーメッセージやラベルを指定しています。
スタイルに関する記述はおこなっていません。

@(userForm: Form[User])

@import helper._
@import helper.twitterBootstrap._
@title = {
    ユーザー登録
}

@main(title, nav = "signup") {
  <div class="container">
      <div class="hero-unit">
        <h1>Bootstrap with Play2.0!</h1>
        <p>このサンプルはBootstrapをPlay2.0から使用しています。</br>これはPlay2.0のFormサンプルを元に作成されています。</p>
        <p><a href="https://github.com/playframework/Play20/wiki/ScalaFormHelpers" 
			  class="btn btn-primary btn-large">Learn more »</a></p>
      </div>

    @helper.form(action = routes.Application.submit) {
      <fieldset>
        <legend>ユーザー登録サンプル</legend>
        
        @inputText(
        userForm("username"), 
        '_label -> "名前", 
        '_help -> "有効なユーザー名を入力してください.",
        '_error -> userForm.globalError
        )
        
        @inputText(
        userForm("email"), '_label -> "email",
        '_help -> "有効なメールアドレスを入力してください."
        )
        
        @inputPassword(
		userForm("password.main"),
        '_label -> "パスワード",
        '_help -> "パスワードは6文字以上にしてください. "
        )
        
        @inputPassword(
        userForm("password.confirm"), 
        '_label -> "パスワード確認",
        '_help -> "もう一度パスワードを入力してください.",
        '_error -> userForm.error("password")
        )
        
      </fieldset>
      
      <div class="actions">
        <input type="submit" class="btn primary" value="登録">
        <a href="@routes.Application.submit" class="btn">キャンセル</a>
      </div>
      
      }
	  
  </div>
}

そしてサブミット結果であるresult.scala.htmlを記述します。

@(user: User)

@title = {
    ユーザー登録結果
}

@main(title, nav = "signup") {
<div class="container">
    
  <div class="hero-unit">
	<h1>Bootstrap with Play2.0!</h1>
	<p>このサンプルはBootstrapをPlay2.0から使用しています。</br>これはPlay2.0のFormサンプルを元に作成されています。</p>
	<p><a href="https://github.com/playframework/Play20/wiki/ScalaFormHelpers" 
		class="btn btn-primary btn-large">Learn more »</a></p>
  </div>

  <h1>入力された情報</h1>
  </br>
  <h3>ユーザー名</h3>  
  <p>
    @user.username
  </p>
  
  <h3>メールアドレス</h3>
  <p>
    @user.email
  </p>
  
  <h3>パスワード</h3>
  <p>
    @user.password
  </p>
</div>
}

routes修正

最後にroutesを修正します。

# Home page
#GET     /                           controllers.Application.index
# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)

GET     /signup                     controllers.Application.form
POST    /signup                     controllers.SignUp.submit

動作確認

アプリケーションを起動してブラウザでアクセスしてみてください。
Bootstrapが適用されていることがわかります。

>[bootstrap] $ compile
>[bootstrap] $ run