目次

Version 2, last updated by hat at Nov 16 14:17 UTC

Enumeration からのラジオボタンリストとドロップダウンメニューの作成

オリジナルコンテンツは、Lift Google グループのこのスレッドです。

このハウツーガイドでは、Scala の Enumeration オブジェクトからラジオボタンリストとドロップダウンメニューを作成する方法を説明しています。

Web サイトで使うことを目的とした次のような Enumeration があったとします。

object ExchangeMethod extends Enumeration {
  val Pickup = Value("Pickup")
  val Ship = Value("Ship")
  val PickupOrShip = Value("Pickup or Ship")
}

ここで、ExchangeMethod.Value の各値を Web フォーム上のラジオボタンまたはドロップダウンメニューのアイテムで表し、コードで Enumeration に要素を追加できるようにしたいとします。たとえば、“Online Transfer” という内容の新しい ExchangeMethod.Value を追加する場合を考えてください。そのような場合、明示的にラジオボタンやリストのアイテムを追加しなくても、サイトの側でラジオボタンリストやドロップダウンメニューで対応できるようにした方が便利です。

基本となる共通のコード

1. フォームには、ラジオボタンまたはドロップダウンの XHTML で置き換えるための “prefix:element” バインドタグを含める必要があります。“prefix” や “element” に何を選ぶかはユーザー次第ですが、一度決めたらその後は適切な bind コードを使う必要があります。次に示すのは、“prefix:element” タグとして “item:exchangeMethod” と “item:submit” を使った場合の例です。

<lift:SnippetName.doSomething form="post" multipart="false">
  ...
  <item:exchangeMethod />
  <item:submit/>
  ...
</lift:SnippetName.doSomething>

2. 基本となる Lift のスニペットは次のようになります。

package com.yourpackage

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

class SnippetName {
  def doSomething (xhtml : NodeSeq) : NodeSeq = { 
    def processForm () : Unit = {...} // フォーム操作用コード

    var chosenMethod: Box[ExchangeMethod.Value] = Empty

    bind("item", xhtml,
         "exchangeMethod" -> ... /* prefix:element タグを何で置き換えるかを記述 */
         "submit" -> SHtml.submit( "Submit Form" /* ボタン名 */, 
                                 processForm /* 呼び出す関数 */)
    )
  }
}

簡単な説明:

  • このスニペットが呼び出されると、“lift:SnippetName.doSomething” タグで囲まれた部分のすべての XHTML が引数 “xhtml” として “doSomething” メソッドに渡されます。
  • “var chosenMethod” は、ExchangeMethod.Value の入った Box です (「Box に関する注」1 を参照してください)。
  • bind は xhtml 内で “item” プリフィックスを探し、指定された要素を “item:exchangeMethod” にバインドし、さらに submit ボタンを processForm 関数にバインドします。“item:exchangeMethod” バインドは、ドロップダウンメニューやラジオボタングループを作成する場所です。
  • 補足: SHtml.submit の引数で渡されている processForm の後にはかっこがなく、processForm メソッドの宣言にはかっこがあります。かっこがないのは、関数を評価しないで使うために引数として渡しているためです (詳細については、Google グループのこのスレッドを参照してください)。

ドロップダウンメニューの作成

次に示すのは、上のスニペットを基に、ドロップダウンメニューを作成するコードです。

class SnippetName {
  def doSomething (xhtml : NodeSeq) : NodeSeq = { 
    def processForm () : Unit = {...} // フォーム操作用コード

    var chosenMethod: Box[ExchangeMethod.Value] = Empty

    bind("item", xhtml,
         "exchangeMethod" -> SHtml.selectObj[ExchangeMethod.Value](
              ExchangeMethod.values.toList.map(v => (v,v.toString)),
              Empty,
              selected => chosenMethod = Full(selected) ),
      ...
    )
  }
}

説明:

  • メソッドのシグニチャーは次のとおりです。
def selectObj[T](
        options : Seq[(T, String)], 
        default : Box[T], 
        onSubmit : (T) => Unit, 
        attrs : (String, String)* )
  • この SelectObj が対象とする型 “T” を忘れないでください。指定を忘れると、コンパイラがうまく処理できずにクラッシュします。現在取り上げている例の場合、型 “T” は “ExchangeMethod.Value” です。
  • “options” は一連のタプル (Value, 文字列表現) です。これを満たすために、ExchangeMethod.values.toList を呼び出して List[ExchangeMethod.Value] を取得します。そして List クラスの “map” メソッドを呼び出してタプルのリストを作成します。
  • “default” は、あらかじめ選択済みの値です。ここでは Empty (EmptyBox シングルトン。「Box に関する注」1を参照してください) を使っています。
  • “onSubmit” は、フォームのサブミット時に実行される関数です。ここでは “selected” 変数で満たされた Full Box を “var chosenMethod” に割り当てています。

ラジオボタンリストの作成

次に示すのは、最初のスニペットを基に、ラジオボタンリストを作成するコードです。

class SnippetName {
  def doSomething (xhtml : NodeSeq) : NodeSeq = { 
    def processForm () : Unit = {...} // フォーム操作用コード

    var chosenMethod: Box[ExchangeMethod.Value] = Empty

    val radios =
      SHtml.radio( ExchangeMethod.values.toList.map(_.toString), Empty,
                   selected =>
                    chosenMethod = Box(ExchangeMethod.withName(selected)) )

    bind("item", xhtml,
         "exchangeMethod" -> radios.toForm,
      ...
    )
  }

}

説明:

  • XHTML は、ほかのバインドタグと同じなので説明する必要はないでしょう。
  • スニペットでは、“val radios” と記述して、次のようなシグニチャーを持つ SHtml.radio メソッドを使っています。
SHtml.radio( 
  radioChoices: Seq[String], 
  defaultVal: Box[String], 
  submitFunction: (String) => Any, 
  attributes: (String, String)* ) 
  • “radioChoices” は、表示されるボタンです。ExchangeMethod.values.toList は List[ExchangeMethod.Value] を返します。リストに対して map メソッドを使って、List[String] に変換し、SHtml.radio メソッドのシグニチャーと一致するようにしています。
  • “defaultVal” は radioChoices のデフォルト値です。ここでは Empty (EmptyBox シングルトン。「Box に関する注」1を参照してください) を使っています。
  • サブミット時に実行される関数では、Box の apply(in: Option[T]) “コンストラクタ” を使って、Option から新しい Box を作成しています。これは、ExchangeMethod.withName(selected: String) が Option を返すためです。
  • radio.toForm を呼び出して、ラジオボタンを XHTML 化します。“val radio” は、実際には ChoiceHolder です (詳細については、ここを参照してください)。実際の “val radio” の構造は次のようになります。
ChoiceHolder( List(
  ChoiceItem(Pickup, ...XHTML...),
  ChoiceItem(Ship, ...XHTML...),
  ChoiceItem(Pickup or Ship, ...XHTML...) ) ) 

1 Box に関する注
Box は、追加の機能を持った Scala の Option クラスのようなものです。Box は基本的に何かを入れておくためのコンテナなので、Null Pointer Exception が発生することはありません。何かが入っている Box を作成するときは Full(object)、空の Box を作成するときは Empty とします。Lift 1.1 以降、Box クラスおよびオブジェクトは、net.liftweb.util から net.liftweb.common に移されています。Lift 1.0 または Lift 1.1 を使っている場合は、注意してください。ただし、この記事では Lift 1.0 の Box に関する説明を利用しました。