Grailsでのプルダウンの作り方をあれこれ悩んだので、今日はその話を。
※Grailsのバージョンは2.3.7です。
やりたいこと
- マスタ値からプルダウンに使う値を取得して、アプリケーションスコープに格納し、それを使ってプルダウンを作成したい。
- プレースフォルダー的な表示をプルダウンでやりたい。
- ブランクの表示
プルダウンの作成
Grailsでのプルダウン作成はg:selectタグを使うと作成できます。 g:selectタグの説明は以下にありますが、
例えば
- あるオブジェクトのvalue属性をプルダウンのkeyとvalueに設定したプルダウンを作って、
- RegisterControllerで設定したパラメータオブジェクトのstatusに設定したい
というような場合は以下のようになります。
<g:form controller="register"> <g:select name="status" from="${application.status}" optionKey="value" optionValue="value" /> </g:form>
ちなみにHTMLは以下のようになります。
<select id="status" name="status"> <option value="未対応" >未対応</option> <option value="再現待ち" >再現待ち</option> <option value="解析済み" >解析済み</option> <option value="対応済み" >対応済み</option> <option value="完了" >完了</option> <option value="無効" >無効</option> <option value="対応なし" >対応なし</option> <option value="次回対応" >次回対応</option> </select>
idは未設定の場合、自動でname属性の値が設定されますが、g:selectタグでid属性を指定することで好きな値にすることもできます。
あえてインデントも出力時のままにしています。
見えないところですが、個人的にはインデントも揃えて出力して欲しかった。
マスタ値の作成とアプリケーションスコープへの格納
マスタ値の登録方法は色々考えられると思いますが、 以下はdevモード動作前提で初期化処理の中で行っています。
class BootStrap { def init = { servletContext -> // マスタへ値設定 CodeMst.withTransaction { new CodeMst(codename:"C0004",label:"ステータス",key:"1",value:"未対応").save() new CodeMst(codename:"C0004",label:"ステータス",key:"2",value:"再現待ち").save() new CodeMst(codename:"C0004",label:"ステータス",key:"3",value:"解析済み").save() new CodeMst(codename:"C0004",label:"ステータス",key:"4",value:"対応済み").save() new CodeMst(codename:"C0004",label:"ステータス",key:"5",value:"完了").save() new CodeMst(codename:"C0004",label:"ステータス",key:"6",value:"無効").save() new CodeMst(codename:"C0004",label:"ステータス",key:"7",value:"対応なし").save() new CodeMst(codename:"C0004",label:"ステータス",key:"8",value:"次回対応").save() } // アプリケーションスコープへの格納 servletContext.setAttribute( "status", CodeMst.findAllByCodename("C0004", [sort:"key", order:"asc"]).collect { new CodeMst(value:it.value) }) } }
解説
BootStrapクラスのinitはGrailsアプリの起動時に読み込まれるので、ここでDBへの登録を行っています。
次に登録した値をDBから取得し、アプリケーションスコープにstatusという名前で格納しています。 DB登録時にwithTransactionを使っているのは、ここの取得処理前までにcommitされていて欲しいからです。
アプリケーションスコープを使っているのは、この値が利用者によらずシステムで共通的に使われるからです。 アプリケーションスコープは便利ですが、マスタ値などを格納して使っている場合は、その値が更新された時の反映手段を作っておかないと 値反映のためにシステムの再起動が必要になるので、利用する際は更新のことを気に留めておいてください。
アプリケーションスコープ格納処理の補足
Grailsのアプリケーションスコープ名は
- GSP上で使う場合はapplication
- ロジック上で使う場合はservletContext
になるようです。
上記サンプルでGSPの取得元がapplication.statusで、BootStrapでの設定がservletContextのstatusなのはこの理由のためです。
またDBからの取得処理は、
- findAll*メソッドを使って対象レコードのカラムをすべて取得した後で、
- 必要なカラムだけを設定したCodeMstを作成し、
- それをアプリケーションスコープにstatusという名前で設定する
ようにしています。
これは早く動作を確認したかったのと、クライテリアを使って書くのが面倒くさかったからなのですが、 この書き方だとDBからの取得データが大きい場合に性能問題が発生する可能性があります。
本来はちゃんとクライテリア使ってプロジェクション(射影)で取得カラムを絞ってください。
上記の取得処理をクライテリアを使って書くと以下のようになります。
def c = CodeMst.createCriteria() def values = c.list { projections { property('value') } eq("codename", "C0004") order("key", "asc") }
ちなみにGrailsデフォルトのDBであるH2DBは以下のアドレスでブラウザからアクセスできます。
http://localhost:8080/<アプリ名>/dbconsole/
ブランクの追加
プルダウンにブランクを追加したい場合は、noSelection属性に['':'']を指定することで実現できます。
※空のMap([:])だとエラーになります。
<g:select name="status" from="${application.status}" optionKey="value" optionValue="value" noSelection="['':'']"/>
プルダウンにプレースフォルダーを追加
プレースフォルダー。
つまり初期表示はされるがプルダウンの中には存在しない項目を作る場合は、optionタグを自分で追加する必要があります。
<g:form controller="register"> <select name="status"> <option value='' disabled selected style='display:none;'>ステータスを入れて!</option> <g:each in="${application.status}"> <option value="${it.value}">${it.value}</option> </g:each> </select> </g:form>
プレースフォルダー自体は意外と簡単にできますが、あとはプレースフォルダーの時は薄い色にするとかしたいですね。
これもHTML出力をサンプルで載せておきます。
<select name="status"> <option value='' disabled selected style='display:none;'>ステータスを入れて!</option> <option value="未対応" >未対応</option> <option value="再現待ち" >再現待ち</option> <option value="解析済み" >解析済み</option> <option value="対応済み" >対応済み</option> <option value="完了" >完了</option> <option value="無効" >無効</option> <option value="対応なし" >対応なし</option> <option value="次回対応" >次回対応</option> </select>
長くなりましたが、読み返すと大したこと言ってませんね!プルダウン表示の説明にどれだけ時間をかけるのだと。
世間はJava8やScalaで盛り上がってる感がありますが、自分はしばらくgroovyかなーという感じです。 最近の目標はGitHub Kaigiまでに作成中のアプリを形にすることですね。頑張ります。