前回 は、Spring MVC(TERASOLUNA 5)にBootstrap 3を適用する方法を解説しましたが、今回は入力フォームの実装方法を解説してみたいと思います。
アプリのベースとしては、TERASOLUNA 5のブランクプロジェクトを使用しますが、基本的にSpring MVCアプリには適用できます。
今回使用するライブラリ執筆時点で、Bootstrapの最新verは4.1.3です。HTMLに適用するクラス名に違いはありますが、基本的に同じノリで適用できるはずですが、マイグレーションが難しそうなら別に記事を書くかもしれません。
(前置き)Bootstrapのパネルを適用する入力フォームとは直接関係ありませんが、画面にパネルを適用することで、画面の表示項目を整理することができます。
<div id="wrapper" class="container-fluid">
<div class="panel panel-default">
<div class="panel-heading"><!-- 入力フォームのタイトル --></div>
<div class="panel-body">
<!-- ここに入力フォームを実装する -->
親div
タグにpanel
クラスを付与すると、パネルが生成される。
子div
タグにpanel-heading
クラスを付与すると、パネルのタイトルになる。
子div
タグにpanel-body
クラスを付与すると、パネルのコンテンツになる。
panel-default
はパネルの色を指定するクラスで、以下の色が指定できます。
* -primary
(青)
* -success
(緑)
* -info
(水色)
* -warning
(黄色)
* -danger
(赤)
以降は、パネルのコンテンツに入力フォームを実装していきます。`
ベーシックな入力フォームを実装する
まずは、ベーシックな入力フォームを実装してみます。
期待するHTML
<form method="post" action="/sample">
<!-- (1) -->
<div class="form-group">
<label for="input" class="control-label">Input</label>
<input id="input" name="input" class="form-control" placeholder="Input" />
<span id="input.errors" class="help-block">Error Message</span>
<!-- (2) -->
<button type="submit" class="btn btn-primary">Submit</button>
<button type="reset" class="btn btn-default">Reset</button>
</form>
入力項目のラベル・入力フィールド・エラーメッセージをdiv
タグで囲み、form-group
クラスを付与する。
ラベルにcontrol-label
クラスを付与する。
入力フィールドにform-control
クラスを付与する。
エラーメッセージにhelp-block
クラスを付与する。
これを1セットとして、すべての入力項目に適用すると、Bootstrapにより入力項目がきれいに並べられます。
入力フィールドのplaceholder
で、入力例や入力条件を示しておくと、ユーザビリティがアップします。
ボタンにbtn
クラスを付与する。
これにより、ボタンやリンクをBootstrapのボタンデザインに統一することができます。
ボタンの色と位置のルールを統一すると、アプリのアクセシビリティがアップします。
* ポジティブなアクションのボタンは青系、ネガティブなアクションのボタンは赤系とか
* ポジティブなアクションのボタンは右側、ネガティブなアクションのボタンは左側とか
JSPの実装
<!-- (1) -->
<form:form method="post" action="${pageContext.request.contextPath}/sample"
modelAttribute="inputForm">
<!-- (2) -->
<div class="form-group">
<form:label path="input" cssClass="control-label">Input</form:label>
<form:input path="input" cssClass="form-control" placeholder="Input" />
<form:errors path="input" cssClass="help-block" />
<!-- (3) -->
<form:button class="btn btn-primary">Submit</form:button>
<form:button class="btn btn-default" type="reset">Reset</form:button>
</form:form>
form:form
タグを利用して、モデルとバインドする。
form:label
タグのcssClass
にcontrol-label
クラスを付与する。
form:input
タグのcssClass
にform-control
クラスを付与する。
form:errors
タグのcssClass
にhelp-block
クラスを付与する。
id
、name
はpath
から自動的に付与されます。
form:button
タグではclass
にbtn
クラスを付与する。
form:button
タグにはcssClass
がないので、注意しましょう。
type="submit"
は自動的に付与されるので、それ以外にしたいときだけtype
を付与します。
Inlineな入力フォームを実装する
すべての入力項目を一行で表現する入力フォームを実装します。
期待するHTML
<form method="post" action="/sample" class="form-inline">
</form>
form
タグにform-inline
クラスを付与する。
これだけです。
JSPの実装
<form:form method="post" action="${pageContext.request.contextPath}/sample"
modelAttribute="inputForm" class="form-inline">
</form:form>
JSPも同じように実装できます。
Horizontalな入力フォームを実装する
ひとつの入力項目を一行で表現する入力フォームを実装します。
期待するHTML
<!-- (1) -->
<form method="post" action="/sample" class="form-horizontal">
<!-- (2) -->
<div class="form-group">
<label for="input" class="control-label col-sm-2">Input</label>
<div class="col-sm-10">
<input id="input" name="input" class="form-control" placeholder="Input" />
<span id="input.errors" class="help-block">Error Message</span>
<!-- (3) -->
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Submit</button>
<button type="reset" class="btn btn-default">Reset</button>
</form>
form
タグにform-horizontal
クラスを付与する。
col-sm-2
クラスを付与して、ラベルと入力フィールドの表示位置を調整する。
Bootstrapでは1行を12分割して表示位置を調整することができ、col-sm-
の合計が12になるよう数字を調整するだけでOKです。
入力フィールドはdiv
で囲んでcol-sm-
を付与する必要があることに注意しましょう。
ボタンをform-group
のdiv
で囲み、さらにcol-sm-
のdiv
で表示位置を調整する。
col-sm-offset-
は見たままですが、オフセットを指定してボタンを入力フィールドと並べます。
JSPの実装
<form:form method="post" action="${pageContext.request.contextPath}/sample"
modelAttribute="inputForm" class="form-horizontal">
<div class="form-group">
<form:label path="input" cssClass="control-label col-sm-2">Input</form:label>
<div class="col-sm-10">
<form:input path="input" cssClass="form-control" placeholder="Input" />
<form:errors path="input" cssClass="help-block" />
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<form:button class="btn btn-primary">Submit</form:button>
<form:button class="btn btn-default" type="reset">Reset</form:button>
</form:form>
JSPも同じように実装できます。
入力チェックエラー時にスタイルを変更する
入力チェックエラーになった入力項目(ラベル・入力フィールド・エラーメッセージ)を赤く表示してみます。
期待するHTML
<form method="post" action="/sample">
<!-- (1) -->
<div class="form-group has-error">
<label for="input" class="control-label">Input</label>
<input id="input" name="input" class="form-control" placeholder="Input" />
<span id="input.errors" class="help-block">Error Message</span>
<button type="submit" class="btn btn-primary">Submit</button>
<button type="reset" class="btn btn-default">Reset</button>
</form>
入力項目を囲むdiv
タグにhas-error
クラスを付与する。
これだけで、ラベル・入力フィールドの枠・エラーメッセージが赤く表示されます。
control-label
、form-control
、help-block
クラスがついていないと色が変わらないので注意。
JSPの実装
<form:form method="post" action="${pageContext.request.contextPath}/sample"
modelAttribute="inputForm">
<!-- (1) -->
<spring:hasBindErrors name="inputForm">
<c:set var="errors" value="${errors}" />
</spring:hasBindErrors>
<!-- (2) -->
<div class="form-group ${errors.hasFieldErrors('input') ? 'has-error' : ''}">
<form:label path="input" cssClass="control-label">Input</form:label>
<form:input path="input" cssClass="form-control" placeholder="Input" />
<form:errors path="input" cssClass="help-block" />
<form:button class="btn btn-primary">Submit</form:button>
<form:button class="btn btn-default" type="reset">Reset</form:button>
</form:form>
spring:hasBindErrors
タグでエラー情報を取得して、c:set
タグでページ変数に登録する。
入力チェックエラーかどうかを判定する必要がありますが、エラー情報を参照するためには一工夫必要です。
spring:hasBindErrors
タグは、エラーがあるときだけ有効になり、そのタグ内でだけエラー情報を参照可能にするタグです。
spring:hasBindErrors
タグをまっとうに利用して正常時とエラー時の画面を実装するのはかなり面倒なので、エラー情報だけいただいてフレキシブルなエラー判定を可能にします。
これはControllerでmodel.addAttribute("errors", bindingResult)
してるのと同じです。
入力項目を囲むdiv
タグでEL式を利用して、エラーのときだけhas-error
クラスを付与する。
errors
はController引数で扱うのと同じBindingResult
オブジェクトです。
BindingResult#hasFieldErrors(フィールド名)
で、入力チェックエラーかどうかを判定することができます。
入力チェックエラーメッセージをツールチップに表示する
エラーメッセージをツールチップに表示してみます。
通常、ツールチップを表示するには、入力フィールドにdata-toggle="tooltip"
とtitle="メッセージ"
を付与しますが、Spring MVCではエラーメッセージを入力フィールドとは別に出力するので、自前でツールチップに表示してあげる必要があります。
JSPの実装
<form:form method="post" action="${pageContext.request.contextPath}/sample"
modelAttribute="inputForm">
<div class="form-group">
<form:label path="input" cssClass="control-label">Input</form:label>
<form:input path="input" cssClass="form-control" placeholder="Input" />
<!-- (1) -->
<form:errors path="input" cssClass="hidden" />
<form:button class="btn btn-primary">Submit</form:button>
<form:button class="btn btn-default" type="reset">Reset</form:button>
</form:form>
<!-- (2) -->
<script type="text/javascript">
$('.form-control').tooltip({
html : true,
title : function() {
return $('#' + $(this).attr('id') + '\\.errors').html();
</script>
画面の最下部で、入力フィールドのツールチップを有効にします。
form-control
クラスが付与された入力フィールドにツールチップに、form:errors
タグで出力したエラーメッセージをセットしています。
form:errors
タグは、[フィールド名].errors
というidでエラーメッセージを出力するので、idでフックしています。
入力部品:select
select項目を実装します。
Bootstrap的には通常の入力フィールドと変わらないので、期待するHTMLは省略します。
JSPの実装
<div class="form-group">
<form:label path="select" cssClass="control-label">Select</form:label>
<form:select path="select" cssClass="form-control">
<form:option value="">Please Select...</form:option>
<form:options items="${selectList}" />
</form:select>
<form:errors path="select" cssClass="help-block" />
入力部品:チェックボックス
チェックボックス項目を実装します。
Spring MVCのform:checkboxes
タグでは実現できないので、少々実装が面倒です。
期待するHTML
<div class="form-group">
<!-- (1) -->
<label class="control-label">CheckBox</label>
<!-- (2) -->
<label for="checkboxes1" class="checkbox-inline">
<input type="checkbox" id="checkboxes1" name="checkboxes1" value="1">[1]
</label>
<label for="checkboxes2" class="checkbox-inline">
<input type="checkbox" id="checkboxes2" name="checkboxes2" value="2">[2]
</label>
<label for="checkboxes3" class="checkbox-inline">
<input type="checkbox" id="checkboxes3" name="checkboxes3" value="3">[3]
</label>
<span id="checkboxes.errors" class="help-block">Error Message</span>
チェックボックス項目全体のラベルは、入力フィールドに紐づけない。
入力フィールドをラベルで囲んで、checkbox-inline
クラスを付与する。
label
タグのfor
とinput
タグのid
がひとつずつリンクしているのがポイントです。
JSPの実装
<div class="form-group">
<!-- (1) -->
<label class="control-label">CheckBox</label>
<!-- (2) -->
<c:forEach items="${selectList}" var="item" varStatus="status">
<label for="checkboxes${status.count}" class="checkbox-inline">
<form:checkbox path="checkboxes" value="${item.key}" />${item.value}
</label>
</c:forEach>
<form:errors path="checkboxes" cssClass="help-block" />
チェックボックス項目全体のラベルは、form:label
でなくlabel
タグを使う。
form:checkboxes
タグは使用せず、自前で繰り返してラベルと入力フィールドを出力する。
form:checkboxes
タグだとlabel
タグにクラスを付与できないので、自前で出力する必要があります。
その際、form:label
タグではfor
にインデックスがつかないので、label
タグを使う必要があります。
入力部品:ラジオボタン
ラジオボタン項目を実装します。
checkbox項目と同様に、Spring MVCのform:radiobuttons
タグでは実現できないので、少々実装が面倒です。
期待するHTML
<div class="form-group">
<!-- (1) -->
<label class="control-label">Radio</label>
<!-- (2) -->
<label for="radio1" class="radio-inline">
<input type="radio" id="radio1" name="radio" value="1">[1]
</label>
<label for="radio2" class="radio-inline">
<input type="radio" id="radio2" name="radio" value="2">[2]
</label>
<label for="radio3" class="radio-inline">
<input type="radio" id="radio3" name="radio" value="3">[3]
</label>
<span id="radio.errors" class="help-block">Error Message</span>
ラジオボタン項目全体のラベルは、入力フィールドに紐づけない。
入力フィールドをラベルで囲んで、radio-inline
クラスを付与する。
label
タグのfor
とinput
タグのid
がひとつずつリンクしているのがポイントです。
JSPの実装
<div class="form-group">
<!-- (1) -->
<label class="control-label">Radio</label>
<!-- (2) -->
<c:forEach items="${selectList}" var="item" varStatus="status">
<label for="radio${status.count}" class="radio-inline">
<form:radiobutton path="radio" value="${item.key}" />${item.value}
</label>
</c:forEach>
<form:errors path="radio" cssClass="help-block" />
ラジオボタン項目全体のラベルは、form:label
でなくlabel
タグを使う。
form:radiobuttons
タグは使用せず、自前で繰り返してラベルと入力フィールドを出力する。
form:radiobuttons
タグだとlabel
タグにクラスを付与できないので、自前で出力する必要があります。
その際、form:label
タグではfor
にインデックスがつかないので、label
タグを使う必要があります。
入力部品:日付
日付項目を実装します。
BootstrapのアシストライブラリBootstrap DatePickerを利用すると、日付選択ダイアログを表示することができます。
Bootstrap DatePickerは、CDNまたはダウンロードして参照します。WebJarsとしては提供されていないようです。
JSPの実装
<!-- BootstrapやJQueryの参照は省略します -->
<!-- (1) -->
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/app/css/bootstrap-datepicker3.min.css">
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/app/js/bootstrap-datepicker.min.js"></script>
</head>
<div class="form-group">
<form:label path="date" cssClass="control-label">Date</form:label>
<!-- (3) -->
<form:input path="date" cssClass="form-control date" placeholder="Date" />
<form:errors path="date" cssClass="help-block" />
<!-- (2) -->
<script type="text/javascript">
$('.date').datepicker({
format : 'yyyy-mm-dd'
</script>
</body>
</html>
Bootstrap DatePickerへの参照を追加します。
ここでは、事前にダウンロードしたCSS・JSファイルを参照しています。
画面の最下部で、Bootstrap DatePickerを有効にします。
ここでは、date
クラスが付与された項目に対してBootstrap DatePickerを有効にしています。
HTMLの特定の要素に紐づけるため、script
タグはbody
タグの終わりに書く必要があります。
オプションで日付フォーマットを指定できるので、入力項目をバインドするフィールドの@DateTimeFormat
と一致させましょう。
他に、選択する日付を制限できたり、様々な言語に対応していたりします。
入力フィールドを、Bootstrap DatePickerを有効にする条件と合致させます。
(2)に従い、date
クラスを付与しています。
今回は、Spring MVC(TERASOLUNA 5)アプリでBootstrap 3を利用した入力フォームを実装する方法をまとめてみました。
BootstrapのようなCSSフレームワークを使うと、手軽に画面デザインを洗練することができて良いのですが、Spring MVC+JSPのように一部のHTMLを自動的に構築してくれるフレームワークとはかみ合わない場合が多々あります。
このような問題を解決するポイントは、CSSフレームワークとAPフレームワーク双方が意図するHTMLの構造を把握することにあります。これを踏まえて、CSSフレームワークを使用するかどうかを検討すると良いですね。
次回は、Bootstrap 3を利用したテーブル表示の実装方法についてまとめます。
Bootstrap 3 Docs / CSS / Forms
Bootstrap 3 Docs / Components / Panels
Bootstrap DatePicker Docs