Spring Boot のテンプレートエンジンとしては、こちらで使用方法を把握した Thymeleaf が有名です。本ページでは、フォーム関連の処理について、基本的なサンプルコードをまとめます。Rails におけるビューヘルパーや、フォーム入力値のバリデーションに相当する機能です。
公式ドキュメント
.
|-- build.gradle
|-- gradle
| `-- wrapper
| |-- gradle-wrapper.jar
| `-- gradle-wrapper.properties
|-- gradlew
|-- gradlew.bat
`-- src
`-- main
|-- java
| `-- hello
| |-- Application.java
| |-- Greeting.java
| `-- GreetingController.java
`-- resources
`-- templates
|-- greeting.html
`-- result.html
build.gradle および Application.java については、こちらのページと同じ内容です。
コントローラとビューで値のやり取りを行うための、入れ物となるクラスです。id
や content
は HTML ファイル内の form 内の input に対応しています。
package hello;
public class Greeting {
private long id;
private String content;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
こちらのページにおける @RequestMapping(path = "/greeting", method = RequestMethod.GET)
に相当するアノテーション @GetMapping("/greeting")
を利用しています。@GetMapping
側のアクションについては、Model model
に空の Greeting オブジェクトを格納して greeting ビューで利用しています。@PostMapping
側のアクションについては、@ModelAttribute
が付与された Greeting greeting
に POST されたパラメータが自動で格納されており、result ビューで利用されます。
package hello;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class GreetingController {
@GetMapping("/greeting")
public String greetingForm(Model model) {
model.addAttribute("greeting", new Greeting());
return "greeting";
}
@PostMapping("/greeting")
public String greetingSubmit(@ModelAttribute Greeting greeting) {
// return "greeting"; // 同じビューを使い回すこともできます。
return "result";
}
}
こちらのページにも記載した @{}
を利用してリンクすることで、例えばアプリケーション全体が /myapp
といったプレフィックス以下で動作する場合にも対応できます。また、th:field
はモデルのメンバ変数と input をバインドするための設定です。
Relative URLs starting with / (eg: /order/details) will be automatically prefixed by the application context name.
http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#link-urls
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Form</h1>
<form action="#" th:action="@{/greeting}" th:object="${greeting}" method="post">
<p>Id: <input type="text" th:field="*{id}" /></p>
<p>Message: <input type="text" th:field="*{content}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</body>
</html>
POST された値を Thymeleaf の記法にしたがって設定して表示します。ここまで内容は http://localhost:8080/greeting にアクセスして動作確認できます。
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Result</h1>
<p th:text="'id: ' + ${greeting.id}" />
<p th:text="'content: ' + ${greeting.content}" />
<a href="/greeting">Submit another message</a>
</body>
</html>
input タグには id が自動で付与されて、th:for
から参照されます。
greeting.html
<div>
<label th:for="${#ids.next('admin')}">Admin</label>
<input type="checkbox" th:field="*{admin}" />
</div>
Greeting.java
private boolean isAdmin;
public boolean isAdmin() {
return isAdmin;
}
public void setAdmin(boolean isAdmin) {
this.isAdmin = isAdmin;
}
先程の例の #ids.next()
ではなく #ids.prev()
を用いると、label タグを input タグの後ろに設置できます。また、先程の例の isAdmin と異なり、今回の features は boolean ではないため、複数チェックボックスに対してそれぞれ th:value
で値を設定しています。
greeting.html
<ul>
<li th:each="feat : ${allFeatures}">
<input type="checkbox" th:field="*{features}" th:value="${feat}" />
<label th:for="${#ids.prev('features')}"
th:text="${feat}">feature label</label>
</li>
</ul>
Greeting.java
private List<String> features;
public List<String> getFeatures() {
return features;
}
public void setFeatures(List<String> features) {
this.features = features;
}
GreetingController.java
@GetMapping("/greeting")
public String greetingForm(Model model) {
model.addAttribute("greeting", new Greeting());
model.addAttribute("allFeatures", Arrays.asList("xxx", "yyy"));
return "greeting";
}
@PostMapping("/greeting")
public String greetingSubmit(@ModelAttribute Greeting greeting, Model model) {
greeting.setId(greeting.getId() + 1);
model.addAttribute("allFeatures", Arrays.asList("xxx", "yyy"));
return "greeting";
}
ラジオボタンは、複数チェックボックスの場合と似た記法になります。ただし、モデルが保持する値は単数です。
greeting.html
<ul>
<li th:each="feat : ${allFeatures}">
<input type="radio" th:field="*{feature}" th:value="${feat}" />
<label th:for="${#ids.prev('feature')}" th:text="${feat}">feature label</label>
</li>
</ul>
Greeting.java
private String feature;
public String getFeature() {
return feature;
}
public void setFeature(String feature) {
this.feature = feature;
}
GreetingController.java
(複数チェックボックスの場合と同じであるため、省略。)
セレクトボックスはラジオボタンとほぼ同じ構造になります。以下の例において、Greeting.java と GreetingController.java はラジオボタンと同じであるため省略します。
greeting.html
<select th:field="*{feature}">
<option th:each="feat : ${allFeatures}"
th:value="${feat}"
th:text="${feat}"></option>
</select>
Validating Form Input に記載の org.hibernate:hibernate-validator
は、org.springframework.boot:spring-boot-starter-thymeleaf
が依存するため build.gradle
に明記する必要はありません。依存関係は以下のコマンドで確認できます。
./gradlew dependencies