これまでこのブログで記載してきたSpring Bootアプリケーションのソースコードは、コードリストの値をFormクラスに記載していたが、FormクラスやControllerクラスを経由しなくても、Springのコンテキスト管理下にあるコードリストの値をThymeleafで直接参照することができる。
今回は、Formクラスに記載していたコードリストの値をThymeleafで直接参照するようにしてみたので、そのサンプルプログラムを共有する。
前提条件
下記記事の実装が完了していること。
サンプルプログラムの構成
作成したサンプルプログラムの構成は以下の通り。

なお、上記の赤枠は、前提条件のプログラムから今回追加・変更したプログラムである。
DemoCodeMapクラスの内容は以下の通りで、DemoFormクラス・SearchFormクラスに記載していたコードリストの内容を、Springのコンテキスト管理下に記載している。
package com.example.demo;
import org.springframework.stereotype.Repository;
import java.util.LinkedHashMap;
import java.util.Map;
@Repository("demoCodeMap")
public class DemoCodeMap {
/** 生年月日_月のMapオブジェクト */
public Map<String,String> getMonthItems(){
Map<String, String> monthMap = new LinkedHashMap<String, String>();
for(int i = 1; i <= 12; i++){
monthMap.put(String.valueOf(i), String.valueOf(i));
}
return monthMap;
}
/** 生年月日_日のMapオブジェクト */
public Map<String,String> getDayItems(){
Map<String, String> dayMap = new LinkedHashMap<String, String>();
for(int i = 1; i <= 31; i++){
dayMap.put(String.valueOf(i), String.valueOf(i));
}
return dayMap;
}
/** 性別のMapオブジェクト */
public Map<String,String> getSexItems(){
Map<String, String> sexMap = new LinkedHashMap<String, String>();
sexMap.put("1", "男");
sexMap.put("2", "女");
return sexMap;
}
}DemoFormクラス・SearchFormクラスの内容は以下の通りで、DemoCodeMapクラスに記載したコードリストを削除している。
package com.example.demo;
import com.example.demo.check.CheckDate;
import com.example.demo.check.CheckZenkaku;
import com.example.demo.check.FutureDate;
import lombok.Data;
import org.thymeleaf.util.StringUtils;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
//日付チェック・未来日チェックを独自アノテーションで実施
@Data
@CheckDate(dtYear = "birthYear", dtMonth = "birthMonth", dtDay = "birthDay"
, message = "{validation.date-invalidate}")
@FutureDate(dtYear = "birthYear", dtMonth = "birthMonth", dtDay = "birthDay"
, message = "{validation.date-future}")
public class DemoForm implements Serializable {
/** ID */
private String id;
/** 名前 */
@NotEmpty
@CheckZenkaku
private String name;
/** 生年月日_年 */
private String birthYear;
/** 生年月日_月 */
private String birthMonth;
/** 生年月日_日 */
private String birthDay;
/** 性別 */
@NotEmpty
private String sex;
/** メモ */
private String memo;
/** 確認チェック */
@NotEmpty
private String checked;
/** 性別(文字列) */
private String sex_value;
/**
* 生年月日の年・月・日が入力されているかをチェックする
* @return チェック結果
*/
@AssertTrue(message = "{validation.date-empty}")
public boolean isBirthDayRequired(){
if(StringUtils.isEmpty(birthYear)
&& StringUtils.isEmpty(birthMonth)
&& StringUtils.isEmpty(birthDay)){
return false;
}
return true;
}
/**
* 性別が不正な値でないかチェックする
* @return チェック結果
*/
@AssertTrue(message = "{validation.sex-invalidate}")
public boolean isSexInvalid(){
return StringUtils.isEmpty(sex)
|| new DemoCodeMap().getSexItems().keySet().contains(sex);
}
}package com.example.demo;
import com.example.demo.check.CheckDate;
import com.example.demo.check.CheckFromToDate;
import lombok.Data;
//生年月日_from,toの日付チェックを独自アノテーションで実施
@Data
@CheckDate(dtYear = "fromBirthYear", dtMonth = "fromBirthMonth"
, dtDay = "fromBirthDay", message = "{validation.date-invalidate-from}")
@CheckDate(dtYear = "toBirthYear", dtMonth = "toBirthMonth"
, dtDay = "toBirthDay", message = "{validation.date-invalidate-to}")
@CheckFromToDate(dtYearFrom = "fromBirthYear", dtMonthFrom = "fromBirthMonth"
, dtDayFrom = "fromBirthDay", dtYearTo = "toBirthYear"
, dtMonthTo = "toBirthMonth", dtDayTo = "toBirthDay"
, message = "{validation.date-invalidate-from-to}")
public class SearchForm {
/** 検索用名前 */
private String searchName;
/** 生年月日_年_from */
private String fromBirthYear;
/** 生年月日_月_from */
private String fromBirthMonth;
/** 生年月日_日_from */
private String fromBirthDay;
/** 生年月日_年_to */
private String toBirthYear;
/** 生年月日_月_to */
private String toBirthMonth;
/** 生年月日_日_to */
private String toBirthDay;
/** 検索用性別 */
private String searchSex;
/** 一覧画面の現在ページ数 */
private int currentPageNum;
}検索画面(search.html)の内容は以下の通りで、例えば「*{getMonthItems()}」と記載していた箇所を、「${@demoCodeMap.getMonthItems()}」と修正している。
<!DOCTYPE html>
<html lang="jp" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link th:href="@{/demo.css}" rel="stylesheet" type="text/css" />
<title>index page</title>
</head>
<body>
<p>検索条件を指定し、「検索」ボタンを押下してください。</p><br/>
<form method="post" th:action="@{/search}" th:object="${searchForm}">
<!-- 2行エラーがある場合は、エラーメッセージを改行して表示 -->
<span th:if="*{#fields.hasErrors('fromBirthYear')}"
th:errors="*{fromBirthYear}" class="errorMessage"></span>
<span th:if="*{#fields.hasErrors('fromBirthYear') "
+ "&& #fields.hasErrors('toBirthYear')}">
<br/>
</span>
<span th:if="*{#fields.hasErrors('toBirthYear')}"
th:errors="*{toBirthYear}" class="errorMessage"></span>
<table border="1" cellpadding="5">
<tr>
<th>名前</th>
<td><input type="text" th:value="*{searchName}"
th:field="*{searchName}" /></td>
</tr>
<tr>
<th>生年月日</th>
<td><input type="text" th:value="*{fromBirthYear}" size="4"
maxlength="4" th:field="*{fromBirthYear}"
th:errorclass="fieldError"/>年
<select th:field="*{fromBirthMonth}" th:errorclass="fieldError"
th:classappend="${#fields.hasErrors('fromBirthYear')} "
+ "? 'fieldError'">
<option value=""></option>
<option th:each="item : ${@demoCodeMap.getMonthItems()}"
th:value="${item.key}" th:text="${item.value}" />
</select>月
<select th:field="*{fromBirthDay}" th:errorclass="fieldError"
th:classappend="${#fields.hasErrors('fromBirthYear')} "
+ "? 'fieldError'">
<option value=""></option>
<option th:each="item : ${@demoCodeMap.getDayItems()}"
th:value="${item.key}" th:text="${item.value}" />
</select>日~
<input type="text" th:value="*{toBirthYear}" size="4"
maxlength="4" th:field="*{toBirthYear}"
th:errorclass="fieldError"/>年
<select th:field="*{toBirthMonth}" th:errorclass="fieldError"
th:classappend="${#fields.hasErrors('toBirthYear')} "
+ "? 'fieldError'">
<option value=""></option>
<option th:each="item : ${@demoCodeMap.getMonthItems()}"
th:value="${item.key}" th:text="${item.value}" />
</select>月
<select th:field="*{toBirthDay}" th:errorclass="fieldError"
th:classappend="${#fields.hasErrors('toBirthYear')} "
+ "? 'fieldError'">
<option value=""></option>
<option th:each="item : ${@demoCodeMap.getDayItems()}"
th:value="${item.key}" th:text="${item.value}" />
</select>日
</td>
</tr>
<tr>
<th>性別</th>
<td>
<select th:field="*{searchSex}">
<option value=""></option>
<option th:each="item : ${@demoCodeMap.getSexItems()}"
th:value="${item.key}" th:text="${item.value}" />
</select>
</td>
</tr>
</table>
<br/><br/>
<input type="submit" value="検索" /><br/><br/>
<input type="button" value="閉じる" onclick="window.close();" />
</form>
</body>
</html> 入力画面(input.html)・確認画面(confirm.html)・削除確認画面(confirm_delete.html)の内容は以下の通りで、検索画面と同じ修正を行っている。
<!DOCTYPE html>
<html lang="jp" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link th:href="@{/demo.css}" rel="stylesheet" type="text/css" />
<title>入力画面</title>
</head>
<body>
<p>下記必要事項を記載の上、「確認」ボタンを押下してください。</p><br/>
<form method="post" th:action="@{/confirm}" th:object="${demoForm}">
<table border="0">
<tr>
<td align="left" valign="top">名前:</td>
<td>
<input type="text" th:value="*{name}"
th:field="*{name}" th:errorclass="fieldError" />
<span th:if="*{#fields.hasErrors('name')}"
th:errors="*{name}" class="errorMessage"></span>
</td>
</tr>
<tr>
<td align="left" valign="top">生年月日:</td>
<td>
<input type="text" th:value="*{birthYear}" size="4"
maxlength="4" th:field="*{birthYear}"
th:errorclass="fieldError"
th:classappend="${#fields.hasErrors('birthDayRequired')} "
+ "? 'fieldError'" />年
<select th:field="*{birthMonth}" th:errorclass="fieldError"
th:classappend="${#fields.hasErrors('birthYear')
|| #fields.hasErrors('birthDayRequired')} "
+ "? 'fieldError'">
<option value="">---</option>
<option th:each="item : ${@demoCodeMap.getMonthItems()}"
th:value="${item.key}" th:text="${item.value}" />
</select>月
<select th:field="*{birthDay}" th:errorclass="fieldError"
th:classappend="${#fields.hasErrors('birthYear')
|| #fields.hasErrors('birthDayRequired')} "
+ "? 'fieldError'">
<option value="">---</option>
<option th:each="item : ${@demoCodeMap.getDayItems()}"
th:value="${item.key}" th:text="${item.value}" />
</select>日
<span th:if="*{#fields.hasErrors('birthDayRequired')}"
th:errors="*{birthDayRequired}" class="errorMessage"></span>
<span th:if="*{#fields.hasErrors('birthYear')}"
th:errors="*{birthYear}" class="errorMessage"></span>
</td>
</tr>
<tr>
<td align="left" valign="top">性別:</td>
<td>
<for th:each="item : ${@demoCodeMap.getSexItems()}">
<input type="radio" name="sex" th:value="${item.key}"
th:text="${item.value}" th:field="*{sex}"
th:errorclass="fieldError" />
</for>
<span th:if="*{#fields.hasErrors('sex')}"
th:errors="*{sex}" class="errorMessage"></span>
<span th:if="*{#fields.hasErrors('sexInvalid')}"
th:errors="*{sexInvalid}" class="errorMessage"></span>
</td>
</tr>
<tr>
<td align="left" valign="top">メモ:</td>
<td>
<textarea rows="6" cols="40" th:value="*{memo}"
th:field="*{memo}"></textarea>
</td>
</tr>
<tr>
<td align="left" valign="top">入力確認:</td>
<td>
<input type="checkbox" name="checked" th:value="確認済"
th:field="*{checked}" th:errorclass="fieldError" />
<span th:if="*{#fields.hasErrors('checked')}"
th:errors="*{checked}" class="errorMessage"></span>
</td>
</tr>
</table>
<br/><br/>
<input type="submit" name="next" value="確認" />
<input type="submit" name="back" value="戻る" />
</form>
</body>
</html><!DOCTYPE html>
<html lang="jp" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>確認画面</title>
</head>
<body>
<p>入力内容を確認し、問題なければ「送信」ボタンを押下してください。</p>
<form method="post" th:action="@{/send}" th:object="${demoForm}">
<table border="0">
<tr>
<td align="left" valign="top">名前: </td>
<td>
<span th:text="*{name}">
ここに名前が表示されます
</span>
</td>
</tr>
<tr>
<td align="left" valign="top">生年月日: </td>
<td>
<span th:text="*{birthYear} + '年'
+ ${@demoCodeMap.getMonthItems().get('__*{birthMonth}__')} + '月'
+ ${@demoCodeMap.getDayItems().get('__*{birthDay}__')} + '日'">
ここに生年月日が表示されます
</span>
</td>
</tr>
<tr>
<td align="left" valign="top">性別: </td>
<td>
<span th:text="${@demoCodeMap.getSexItems().get('__*{sex}__')}">
ここに性別が表示されます
</span>
</td>
</tr>
<tr>
<td align="left" valign="top">メモ: </td>
<td>
<th:block th:if="*{memo}">
<th:block th:each="memoStr, memoStat :"
+ " *{memo.split('\r\n|\r|\n', -1)}">
<th:block th:text="${memoStr}"/>
<br th:if="${!memoStat.last}"/>
</th:block>
</th:block>
</td>
</tr>
<tr>
<td align="left" valign="top">確認チェック: </td>
<td>
<span th:text="*{checked}">
ここに確認チェック内容が表示されます
</span>
</td>
</tr>
</table>
<br/><br/>
<input type="submit" name="next" value="送信" />
<input type="submit" name="back" value="戻る" />
</form>
</body>
</html><!DOCTYPE html>
<html lang="jp" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>削除確認画面</title>
</head>
<body>
<p>下記内容を削除してよろしいでしょうか?問題なければ「送信」ボタンを押下してください。</p>
<form method="post" th:action="@{/delete}" th:object="${demoForm}">
<table border="0">
<tr>
<td align="left" valign="top">名前: </td>
<td>
<span th:text="*{name}">
ここに名前が表示されます
</span>
</td>
</tr>
<tr>
<td align="left" valign="top">生年月日: </td>
<td>
<span th:text="*{birthYear} + '年'
+ ${@demoCodeMap.getMonthItems().get('__*{birthMonth}__')} + '月'
+ ${@demoCodeMap.getDayItems().get('__*{birthDay}__')} + '日'">
ここに生年月日が表示されます
</span>
</td>
</tr>
<tr>
<td align="left" valign="top">性別: </td>
<td>
<span th:text="${@demoCodeMap.getSexItems().get('__*{sex}__')}">
ここに性別が表示されます
</span>
</td>
</tr>
<tr>
<td align="left" valign="top">メモ: </td>
<td>
<th:block th:if="*{memo}">
<th:block th:each="memoStr, memoStat : "
+ "*{memo.split('\r\n|\r|\n', -1)}">
<th:block th:text="${memoStr}"/>
<br th:if="${!memoStat.last}"/>
</th:block>
</th:block>
</td>
</tr>
</table>
<br/><br/>
<input type="submit" name="next" value="送信" />
<input type="submit" name="back" value="戻る" />
</form>
</body>
</html>その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-web-codelist-thymeleaf/demo
サンプルプログラムの実行結果
サンプルプログラムを実行すると、以下の記事に記載されているデータ更新・チェック処理・ページング処理が実行される。
実際、以下の画面のように、コードリストの値が設定されていることが確認できる。


要点まとめ
- コードリストの値は、Springのコンテキスト管理下にあれば、Thymeleafで直接参照することができる。





