( ꒪⌓꒪) ゆるよろ日記

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

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メソッドの中でを使ってNodeSeqを生成して返している。


ScalaXMLリテラルで単一のノードを示すためのコンテナのようだ。
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のお話ですよ。