( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

scalaのWebフレームワーク liftで遊ぶ(5) - フォームでもっとこんにちわ世界

目次はこちら。
scalaのWebフレームワーク liftで遊ぶ 目次 - ゆろよろ日記


「もっと!もっと!もっと!こんにちわ!世界!」

ってことで、前回に引き続いての内容を100%やっちゃうよby安田顕

フォームを作る

フォームがないとwebアプリケーションを作るのは厳しいよ。ってか無理じゃね?

ってことで、Liftでフォームの生成と入力値をどうやって処理するかについて。

シンプルなフォーム

まずはsrc/main/webapp/hellForm.htmlを作るよ。

<lift:surround with="default" at="content">
    <h1>Hello Form</h1>
    Hello <lift:HelloForm.who />
    <br/>
    <form>
        <label for="whoField">Who :</label>
        <input type="text" name="whoField"/>
        <input type="submit" value="send"/>
    </form>
</lift:surround>

このフォームに対応するsnippetをHelloFormという名前で作成。

src/main/scala/com/yuroyoro/lift/hellodrawin/snippet/HelloForm.scala

package com.yuroyoro.lift.hellodarwin.snippet

import net.liftweb.http.S

class HelloForm {
  def who = <tt>{S.param("whoField").openOr("")}</tt>
}

おお、ちゃんとテキストボックスが出たよ!って当たり前か。


では、テキストボックスに適当な値を入力して"send"ボタンを押すと…
入力値が出力される。

もっとLift的なやり方で

Liftでは、html内のユーザが指定したxml namespaceのタグを動的に置き換えるbindメソッドがあり、これを利用するのがlift流らしい。


まずは公式ドキュメントどおりにHelloForm2を作成してみる。

src/main/webapp/helloForm2.html

<lift:surround with="default" at="content">
    <h1>Hello Form2</h1>
    <lift:HelloForm2.show form="POST">
        Hello <hello:who/>
        <br/>
        <label for="whoField">Who :</label>
        <hello:whoField/>
        <hello:submit/>
    </lift:HelloForm2.show>
</lift:surround>

src/main/scala/com/yuroyoro/lift/hellodrawin/snippet/HelloForm2.scala

package com.yuroyoro.lift.hellodarwin.snippet

import scala.xml.NodeSeq
import net.liftweb.http.S._
import net.liftweb.http.SHtml._
import net.liftweb.util.Helpers._

class HelloForm2 {
  var who = "world"

  def show(xhtml: NodeSeq): NodeSeq = {
    bind("hello", xhtml,
      "whoField" --> text(who, who = _) % ("size" -> "10") % ("id" -> "whoField"),
      "submit" --> submit(?("Send"), ignore => {println("value:" + who + " :: " + param("whoField"))}),
      "who" --> who
    )
  }
}

  • フォームを作成し、入力されたデータをHelloForm2クラスのshowメソッドに送る。

  • namespaceがhello。bindするときにはnamespaceを指定する。
  • showメソッドの中で呼び出されているbindメソッドに渡された値に置き換えられる。

bindメソッドについて詳しく

  • net.liftweb.util.Helpersオブジェクトで定義されている。
  • 第1引数:namespace ("hello"を指定した場合、html内のと対応する)
  • 第2引数:xhtml (showメソッドが引数で受けとっったNodeSeqオブジェクト)
  • 第3引数以降:置き換えるタグ名と値のリスト(正確にはBindParamオブジェクト)

bindメソッドに渡す引数.

"whoField"だと、を置換する。

  • text(who, who = _)は<input type=text>を出力する。
    • textメソッドはnet.liftweb.http.SHtmlオブジェクトで定義されている。
    • 第1引数は初期値。例では、var who = "world"で設定した文字列を渡している。
    • 第2引数は、submitされたときに呼び出される処理。例では、 who = _ となっている。これは、入力値を"_"で受け取って変数whoに格納するという意味。
    • この第2引数に無名関数とか関数オブジェクトとか を設定できるっぽい。
    • textメソッドの戻り値は、scala.xml.Elemオブジェクトとなる。
  • % ("size" -> "10") : textメソッドの戻り値のElemオブジェクトの内容を変更している。
    • %メソッドは、xml要素の属性を置き換えるメソッド。例では、sizeを10に設定している。戻り値は変更されたscala.xml.Elem。
  • % ("id" -> "whoField")同様に、id属性を追加している。
    • Liftでは、name属性は自動で追加されるそうだ。

"submit"だとを置換する。

  • フォームを送信するためのsubmitボタンを生成する。
    • submitメソッドはnet.liftweb.http.SHtmlオブジェクトで定義されている。
    • 第一引数の?("Send")は、ボタンのラベルを設定する。?()は国際化のための文字列変換メソッド。
    • ignore => {println("value:" + who + " :: " + param("whoField"))}
      • submitされたときに呼び出される処理。{}で無名関数を定義して、内部で変数whoとwhoFieldの内容を標準出力に出している。


で、SiteMapにHelloForm2を追加して動かしてみる。


って動かないっすね。ダマシかよ。
コンソールには、whoFieldの値がEmptyオブジェクトだと出力されている。

value:hogehoge :: Empty

ようは、textメソッドやsubmitメソッドの第2引数の無名関数から参照している変数whoは、
前のリクエストで生成されたHelloForm2オブジェクトのインスタンス変数whoを参照しているのであって、
submitされた時点でのHelloForm2オブジェクトのインスタンス変数whoではないということらしい。


では、リクエストを超えて変数の参照を行うためにはどうすればよいか?
Liftでは、RequestVarクラスを利用する。


RequestVarを使うようにHelloForm2.scalaを修正する。


src/main/scala/com/yuroyoro/lift/hellodrawin/snippet/HelloForm2.scala

package com.yuroyoro.lift.hellodarwin.snippet

import scala.xml.NodeSeq
import net.liftweb.http.S._
import net.liftweb.http.SHtml._
import net.liftweb.http.RequestVar
import net.liftweb.util.Helpers._
import net.liftweb.util.Full

class HelloForm2 {
  object who extends RequestVar(Full("world"))
  
  def show(xhtml: NodeSeq): NodeSeq = {
    bind("hello", xhtml,
        "whoField" --> text(who.openOr(""), v => who(Full(v))) % ("size" -> "10") % ("id" -> "whoField"),
        "submit" --> submit(?("Send"), ignore => {println("value:" + who.openOr("") + " :: " + param("whoField"))}),
        "who" --> who.openOr("")
    )
  }
}

今度はちゃんと動いたよ!