scalaのWebフレームワーク liftで遊ぶ(6) - ステートフルなフォームでもっとこんにちわ世界
目次はこちら。
scalaのWebフレームワーク liftで遊ぶ 目次 - ゆろよろ日記
「こんにちわ!こんにちわ!世界!」
ってことで、またまた前回に引き続いての内容を100%やっちゃうよ。
XHTMLフラグメントをsnippetに組み込む
前回作ったフォームでは、テンプレートの中のXHTMLタグをbindメソッドで置き換えたが、今度はsnippetから動的にXHTML自体を吐いてしまうというもの。
src/main/webapp/helloForm3.html
<lift:surround with="default" at="content"> <h1>Hello Form3</h1> <lift:HelloForm3.show form="POST"/> </lift:surround>
src/main/scala/com/yuroyoro/lift/hellodrawin/snippet/HelloForm3.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 HelloForm3 { object who extends RequestVar(Full("world")) def show(xhtml: NodeSeq): NodeSeq = { <xml:group> Hello {who.openOr("")} <br/> <label for="whoField">Who :</label> { text(who.openOr(""), v => who(Full(v))) % ("size" -> "10") % ("id" -> "whoField") } { submit(?("Send"), ignore => {println("value:" + who.openOr(""))}) } </xml:group> } }
snippetはscala.xml.NodeSeqを返せばよいので、showメソッドの中で
XMLリテラルの中では{}内にscalaコードを埋め込むことができるので、ここで動的に値を出力している。
SiteMapに登録して動かすと、前回と同様の動作をする。
ステートフルなフォーム
ってどういうことかというと、snippetをステートフルにできるらしい。
つまり、RequestVarを使ってリクエストを超えて状態を保持するのではなく、StatefulSnippetを継承すれば簡単にフォーム自体に状態を持たせることができりゅ。
src/main/webapp/helloForm4.html
<lift:surround with="default" at="content"> <h1>Hello Form4</h1> <lift:HelloForm4.show form="POST"/> </lift:surround>
src/main/scala/com/yuroyoro/lift/hellodrawin/snippet/HelloForm4.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.StatefulSnippet import net.liftweb.util.Helpers._ import net.liftweb.util.Full class HelloForm4 extends StatefulSnippet{ val dispatch: DispatchIt = { case "show" => show _ } var who = "world" def show(xhtml: NodeSeq): NodeSeq = { <xml:group> Hello {who} <br/> <label for="whoField">Who :</label> { text(who, v => who = v) % ("size" -> "10") % ("id" -> "whoField") } { submit(?("Send"), ignore => {println("value:" + who)}) } </xml:group> } }
val dispatch: DispatchIt = ... の部分は、メソッド名によって処理を振り分けるための定義である。
これで実行すると、HelloForm3と同じ動作をする。
本当に同じインスタンスを参照しているのか、showメソッドの先頭にログを出力するようにして動作確認してみると、確かに同一のObject IDがログに出力されている。
showメソッドにログを埋め込んだ。
def show(xhtml: NodeSeq): NodeSeq = { println(this) <xml:group> Hello {who} <br/> <label for="whoField">Who :</label> { text(who, v => who = v) % ("size" -> "10") % ("id" -> "whoField") } { submit(?("Send"), ignore => {println("value:" + who)}) } </xml:group> }
出力されたログ。
com.yuroyoro.lift.hellodarwin.snippet.HelloForm4@a083f2 INFO - Service request (GET) /helloForm4 took 141 Milliseconds value:ほげげ com.yuroyoro.lift.hellodarwin.snippet.HelloForm4@a083f2 INFO - Service request (POST) /helloForm4 took 94 Milliseconds
StatefulSnippetをもっと詳しく
net.liftweb.http.StatefulSnippetのソースをのぞいてみる。
StatefulSnippetは、traitで定義されている。
traitってのは、ものすごくおおざっぱにいうとscalaでmixinを実現するためのもので、Javaでいうとinterfaceに実装を持たせることができるようなもの。
だから、StatefulSnippet以外のクラスを継承したsnippetにも、StatefulSnippetの機能を追加することができるってわけ。
class SomeSnippet extends OtherSnippet with StatefulSnippet
で、StatefulSnippetは自動的にhiddenフィールドを出力してインスタンスの参照を保持するようになっている。
よって、リンクなどを使用したい場合は、StatefulSnippetで定義されているlinkメソッドをを使って<a>:タグを出力しなければならないらしい。
確かに、生成されたHTMLにはhiddenが出力されている。
次回は、Ajaxのお話ですよ。