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とほぼ同じなので割愛