2020-06-21 中村
Thymeleafは,SpringBootで推奨されているテンプレートエンジンである.
サーバサイドで画面 (View層)を生成することができる.
REST-APIを呼び出して,JavaScriptで動的に画面を書き換えるやり方とは
アプローチが異なることに注意.
アプリケーションは,あらかじめ画面のひな型となるHTML
(テンプレート)を用意しておく.
テンプレートは,ところどころ穴あき(変数)のHTMLになっており,
@ControllerがDTOとテンプレートを指定すれば,ThymeleafがDTOの
フィールド値をテンプレートの変数に代入して穴埋めし,完全なHTML
にレンダリングしてブラウザに返却する.
@Controllerオブジェクト(@RestControllerではない)が処理結果を
含んだDTOと画面のひな型となるHTMLテンプレートを指示すれば,
ThymeleafがDTOの値をテンプレートの指定された場所に組み込んで
レンダリングする.
テンプレートは,Spring Boot プロジェクトのresources/templates/
の下に,Thymeleafの変数を埋め込んだHTML形式で保存する.
なお,Spring Boot プロジェクトは,変数を含まないHTML(静的なWeb
ページ)を持つこともでき,その場合には,resources/static/ の下に
保存する.
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
コーディングする前に,まずは画面と画面遷移を大まかでよいので, ノート等にスケッチしておくとよいだろう.
画面
画面遷移
画面遷移の処理の流れを,最初の図を参照しながら,確認しよう
健康診断アプリ(ソフトウェア工学第4回課題)の画面設計を行う
サーバサイドで画面を構成する場合,@RestController クラスではなく,
@Controller クラスを定義する (両方あっても構わないが,URLパスが
重ならないように気を付けること)
PersonController.java
@Controller
public class PersonController {
@Autowired
PersonManager pm; //サービスクラス
(以降メソッド定義)
}
各メソッドの定義について,2.2の例題にそって説明する.
登録されているすべての受診者のデータを画面に表示する
@GetMapping("/persons")
public String showAllPersons(Model model) {
List<PersonDto> personList = pm.getAllPersons();
model.addAttribute("plist", personList);
return "list";
}
登録フォームを表示する
@GetMapping("/persons/register")
public String showPersonForm(Model model) {
model.addAttribute("PersonForm", new PersonForm());
return "register";
}
受診者情報をフォームから受け付けてDBに追加する.その後,一覧に戻る
@PostMapping("/persons")
public String addPerson(@ModelAttribute @Validated PersonForm form, BindingResult result, Model model) {
pm.addPerson(form.toEntity());
return "redirect:/persons";
}
更新フォームを表示する
@GetMapping("/persons/{number}/update")
public String showUpdateForm(@PathVariable Long number, Model model) {
PersonDto p = pm.getPerson(number);
model.addAttribute("PersonForm", p);
return "update";
}
画面で更新された受診者情報をフォームから受け付けてDBに追加する.~その後,一覧に戻る
@PostMapping("/persons/{number}/update")
public String updatePerson(@ModelAttribute @Validated PersonForm form, @PathVariable Long number, Model model) {
pm.updatePerson(number, form.toEntity());
return "redirect:/persons";
}
受診者情報を削除する (確認ルーチンは省略している)
@GetMapping("/persons/{number}/delete")
public String deletePerson(@PathVariable Long number, Model model) {
pm.deletePerson(number);
return "redirect:/persons";
}
何の変哲もない,普通のHTML.ボタンを押すとpersonsに移動(GET)する
<!DOCTYPE html>
<html lang="ja">
<head>
<title>健康診断Webアプリケーション</title>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="UTF-8">
</head>
<body>
<h1>健康診断Webアプリケーション</h1>
<div class="title">
<img src="img/shinchou.png" width="250px">
<img src="img/taijuu.png" width="250px">
</div>
<div>
<input type="button" class="simple_square_btn5" onclick="location.href='persons'" value="受信者一覧へ" />
</div>
</body>
</html>
Thymeleafのテンプレート.重要なポイントが含まれる
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>受診者一覧</title>
<link rel="stylesheet" type="text/css" th:href="@{/style.css}">
</head>
<body>
<h1>受診者一覧</h1>
<input type="button" class="simple_square_btn5" th:onclick="|location.href='@{/persons/register}'|" value="新規受診者登録" />
<input type="button" class="simple_square_btn5" th:onclick="|location.href='@{/}'|" value="タイトル画面へ" />
<table class="tablist">
<tr>
<th>No.</th>
<th>受診者氏名</th>
<th>生年月日</th>
<th>身長</th>
<th>体重</th>
<th>BMI</th>
<th>肥満度</th>
<th>コマンド</th>
</tr>
<tr th:each="p: ${plist}">
<td> [[${p.number}]] </td>
<td> [[${p.name}]] </td>
<td> [[${p.birthday}]] </td>
<td> [[${p.height}]] cm </td>
<td> [[${p.weight}]] kg </td>
<td> [[${#numbers.formatDecimal(p.bmi == null ? 0 : p.bmi, 0, 1)}]] </td>
<th:block th:switch="${p.obesity}">
<td th:case="'低体重'" style="background-color: #7c7cfc;">[[${p.obesity}]]</td>
<td th:case="'普通体重'" style="background-color: #7cfc7c;">[[${p.obesity}]]</td>
<td th:case="'肥満 (1度)'" style="background-color: #fcfc7c;">[[${p.obesity}]]</td>
<td th:case="'肥満 (2度)'" style="background-color: #fcbb91;">[[${p.obesity}]]</td>
<td th:case="'肥満 (3度)'" style="background-color: #fc9191;">[[${p.obesity}]]</td>
<td th:case="'肥満 (4度)'" style="background-color: #c08080;">[[${p.obesity}]]</td>
<td th:case="*">[[${p.obesity}]]</td>
</th:block>
<td>
<a th:href=@{/persons/{num}/update(num=${p.number})}>編集</a> |
<a th:href=@{/persons/{num}/delete(num=${p.number})}>削除</a>
</td>
</tr>
</table>
</body>
登録フォーム.
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>受診者情報登録</title>
<link rel="stylesheet" type="text/css" th:href="@{/style.css}">
</head>
<body>
<h1>受診者情報登録</h1>
<form role="form" th:action="@{/persons}" th:object="${PersonForm}" method="post">
<table class="tabform large">
<tr>
<td><label for="number">No: </label></td>
<td><span th:text="${number}"></span></td>
</tr>
<tr>
<td><label for="name">氏名: </label></td>
<td><input class="large" type="text" required th:field="*{name}" /></td>
</tr>
<tr>
<td><label for="birthday">生年月日:</label></td>
<td><input class="large" type="text" required th:field="*{birthday}" /></td>
</tr>
<tr>
<td><label for="height">身長:</label></td>
<td><input class="large" type="number" required min="0" value="160" step=".1" th:field="*{height}" /> cm</td>
</tr>
<tr>
<td><label for="weight">体重:</label></td>
<td><input class="large" type="number" required min="0" value="60" step=".1" th:field="*{weight}" /> kg</td>
</tr>
<tr>
<td colspan="2">
<input type="button" class="large simple_square_btn5" onclick="history.back()" value="戻る" />
<input type="submit" class="large simple_square_btn5" value="登録する" />
</td>
</tr>
</table>
</form>
</body>
</html>
4.3とほぼ同じなので割愛