SpringBoot/Thymeleaf
の編集
https://cs27.org/wiki/kobespiral2021/?SpringBoot/Thymeleaf
[
トップ
] [
編集
|
差分
|
履歴
|
添付
|
リロード
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
]
-- 雛形とするページ --
(no template pages)
[[SpringBoot]] * Thymeleaf を使ったView層の開発 [#ee213c4b] 2021-04-05 中田 コントローラクラスのテンプレを追記 2020-06-21 中村 Thymeleafは,SpringBootで推奨されている''テンプレートエンジン''である.~ サーバサイドで画面 (View層)を生成することができる. REST-APIを呼び出して,JavaScriptで動的に画面を書き換えるやり方とは~ アプローチが異なることに注意. - こちらはクライアントサイドで画面を生成するやり方 - VueやAngularもこちらのアプローチになる. ** テンプレートエンジンとは [#d5922d83] アプリケーションは,あらかじめ画面のひな型となるHTML~ (''テンプレート'')を用意しておく. テンプレートは,ところどころ穴あき(変数)のHTMLになっており,~ @ControllerがDTOとテンプレートを指定すれば,ThymeleafがDTOの~ フィールド値をテンプレートの変数に代入して穴埋めし,完全なHTML~ にレンダリングしてブラウザに返却する. ** Thymeleaf の概要 [#xe97162e] &attachref(thymeleaf.png); @Controllerオブジェクト(@RestControllerではない)が処理結果を~ 含んだDTOと画面のひな型となるHTMLテンプレートを指示すれば,~ ThymeleafがDTOの値をテンプレートの指定された場所に組み込んで~ レンダリングする. テンプレートは,Spring Boot プロジェクトのresources/templates/ ~ の下に,Thymeleafの変数を埋め込んだHTML形式で保存する. なお,Spring Boot プロジェクトは,変数を含まないHTML(静的なWeb~ ページ)を持つこともでき,その場合には,resources/static/ の下に~ 保存する. ** 1. 準備 [#l42d9e14] *** build.gradle に依存性を追加 [#v54a471d] build.gradle のdependenciesに以下を追記. implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' [[SpringBoot/依存]]を参考にして,他のライブラリとの前後関係に注意すること. ** 2. 画面設計 [#e005334d] *** 2.1 画面と画面遷移 [#m0ad9a4c] コーディングする前に,まずは画面と画面遷移を大まかでよいので, ノート等にスケッチしておくとよいだろう. 画面 - どんな画面があるか? - それぞれのユースケースごとに画面があるはず 画面遷移 - 画面間の遷移はどうなっているか? 画面遷移の処理の流れを,最初の図を参照しながら,確認しよう &attachref(./thymeleaf.png,50%); + ユーザがブラウザ上のボタンを押す + ControllerにGETまたはPOSTが発行される + ControllerがServiceを経由してビジネスロジックを実行し,結果を得る + Controllerが結果をModelに属性としてセットする.addAttribute() + Controllerがreturnでテンプレートを''文字列''で指定する(例えばhogeとする) + Thymeleafがtemplatesの中からテンプレート''文字列.html'' (hoge.html)を探しだす. + Thymeleafがhoge.htmlにModelの属性値を流しこみ完全なHTMLを生成して,クライアントに返す + ユーザのブラウザ上に画面が現れる *** 2.2 例題:健康診断Webアプリ [#mcfa36c0] 健康診断アプリ([[ソフトウェア工学第4回課題:http://www27.cs.kobe-u.ac.jp/~masa-n/lecture/newse/04/enshuu04.html]])の画面設計を行う - エンティティは[[SpringBoot/JPA]]を参照のこと *** 画面の例 [#k9142c95] &attachref(screens.png); *** 画面遷移の例 [#vf8a3232] &attachref(transition.png); - &color(red){赤字}; は画面に対応するURLパスを表す - 画面間の遷移のラベルは,[ボタン・リンク押下] / [コントローラへのアクション] を表している - redirect:/persons は,アクション終了後にリダイレクトを行う ** 3. @Controller クラスの構成 [#mffd1943] サーバサイドで画面を構成する場合,@RestController クラスではなく,~ @Controller クラスを定義する (両方あっても構わないが,URLパスが~ 重ならないように気を付けること) PersonController.java @Controller public class PersonController { @Autowired PersonManager pm; //サービスクラス (以降メソッド定義) } 各メソッドの定義について,2.2の例題にそって説明する. *** 3.1 受診者リストの表示 (GET /persons) [#td8773fc] 登録されているすべての受診者のデータを画面に表示する + Serviceに問い合わせて,全受診者のリストを取得し personListとする + 画面モデルに,属性plistを定義し,personListを紐づける + テンプレート list.html を呼び出す. @GetMapping("/persons") public String showAllPersons(Model model) { List<PersonDto> personList = pm.getAllPersons(); model.addAttribute("plist", personList); return "list"; } *** 3.2 登録フォームの表示 (GET /persons/register) [#pe4dff18] 登録フォームを表示する + 空のフォームオブジェクトPersonFormを作成する + 画面モデルに,属性PersonFormを定義し,オブジェクトを紐づける + テンプレート register.html を呼び出す. @GetMapping("/persons/register") public String showPersonForm(Model model) { model.addAttribute("PersonForm", new PersonForm()); return "register"; } *** 3.3 受診者情報の登録 (POST /persons) [#a9e3d4cb] 受診者情報をフォームから受け付けてDBに追加する.その後,一覧に戻る + ページからPOSTされた属性と値のペアを,@ModelAttribute のformに受け取る + @Vaidatedでバリデーションする + エンティティに変換して,登録サービスに渡す addPerson() + /personsにリダイレクトし,一覧ページを表示する @PostMapping("/persons") public String addPerson(@ModelAttribute @Validated PersonForm form, BindingResult result, Model model) { pm.addPerson(form.toEntity()); return "redirect:/persons"; } *** 3.4 更新フォームの表示 (GET /persons/{number}/update) [#xda4e9a8] 更新フォームを表示する + パス・パラメータnumberから番号がnumberの受診者pを検索する + 画面モデルに,属性PersonFormを定義し,pを紐づける + テンプレート update.html を呼び出す @GetMapping("/persons/{number}/update") public String showUpdateForm(@PathVariable Long number, Model model) { PersonDto p = pm.getPerson(number); model.addAttribute("PersonForm", p); return "update"; } *** 3.5 受診者情報を更新する (POST /persons/{number}/update) [#t2789157] 画面で更新された受診者情報をフォームから受け付けてDBに追加する.~その後,一覧に戻る - 注意: 更新なのでPUTで呼び出したい所だが,htmlの<form>ではPOST,GETしか指定できないのであえてPOSTで. + ページからPOSTされた属性と値のペアを,@ModelAttribute のformに受け取る + @Vaidatedでバリデーションする + パスパラメータから,受診者番号numberを取得する + formをエンティティに変換して,受診者番号numberとともに更新サービスに渡す updatePerson() + /personsにリダイレクトし,一覧ページを表示する @PostMapping("/persons/{number}/update") public String updatePerson(@ModelAttribute @Validated PersonForm form, @PathVariable Long number, Model model) { pm.updatePerson(number, form.toEntity()); return "redirect:/persons"; } *** 3.6 受診者情報を削除する (GET /persons/{number}/delete) [#p17e6509] 受診者情報を削除する (確認ルーチンは省略している) - 注意: 削除なのでDELETEで呼び出したい所だが,クリックで簡単に削除したいのでGETで. + パスパラメータから,受診者情報numberを取得する + 削除サービスを呼び出す deletePerson + /personsにリダイレクトし,一覧ページを表示する @GetMapping("/persons/{number}/delete") public String deletePerson(@PathVariable Long number, Model model) { pm.deletePerson(number); return "redirect:/persons"; } ** 4. HTMLテンプレートを構成する [#p57092b5] *** 4.1 扉ページ (static/index.html) [#g69a446e] 何の変哲もない,普通の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> *** 4.2 受診者一覧 (templates/list.html) [#u81ade37] 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> *** 解説 [#x1a6b6bd] - ''<html>タグ:'' xmlns:th="http://www.thymeleaf.org" の指定 -- Thymeleafの名前空間 th を定義する.これは必須のおまじない. - ''th: がついている部分''は,Thymeleafが動的に書き換える部分 -- (テンプレート) th:属性名 = "[[Thymeleaf式:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html#%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%80%E3%83%BC%E3%83%89%E5%BC%8F%E6%A7%8B%E6%96%87]]" と書いておくと... -- (実行時) 属性名 = [[Thymeleaf式:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html#%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%80%E3%83%BC%E3%83%89%E5%BC%8F%E6%A7%8B%E6%96%87]]が評価された値 に展開される - ''変数式:'' Contollerから渡された変数の値を取り出す -- <td th:text="${p.name}"></td> と書いておくと (pはControllerから渡されたPersonオブジェクト), -- <td>仲村 匡日出</td> と展開される. - ''インライン式:'' 式をhtmlの中に直接書く -- <td> [ [${p.name}] ]</td> - ''リンク式:'' リンクのコンテキストパスをあんじょうやってくれる -- パス・パラメータ{}は()の中で指定する(C言語のprintf()的な感じ) -- <a th:href="@{/persons/{num}/update(num=${p.number})}">編集</a> と書いておくと -- <a href="/person/1/update">編集</a> と展開される - ''リテラル置換'': |・・・| で囲まれた部分を文字列に展開する - ''三項演算子式:'' 条件に応じて,値を切り替える -- 式1 ? 値1: 値2 -- 式1がtrueの場合,値1に評価される.falseの場合,値2に評価される - ''ブロック'' -- <th:block> </th:block>でテンプレート内のブロックを定義できる -- テンプレートを構造化する用途に使う→(繰り返し,分岐,スコープ制限など) -- 実行時には消える - ''繰り返し'' : リストから一つずつ取り出して表示 -- <タグ th:each="p: ${plist}"> ... </タグ> と書いておくと, -- plistから要素p0,p1,...,pnを一つずつ取り出して, -- <タグ> p_0を展開した内容 </タグ> <タグ> p_1を展開した内容</タグ>...<タグ> p_nを展開した内容</タグ> に展開する - ''条件'': 式が成立した時だけ表示 -- <タグ th:if="式">式がtrueのときだけこの内容が出ます</タグ> -- <タグ th:unless="式">式がfalseのときだけこの内容が出ます</タグ> - ''スイッチ'': 式の値によって分岐 -- <th:block th:switch="式1"> --- <タグ th:case="式2">式1==式2の時だけこの内容が出ます</タグ> ** 4.3 受診者登録 (templates/register.html) [#ma1e1871] 登録フォーム. <!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> *** 解説 [#a467ef52] - Controllerから空のフォームを''PersonForm''という属性名で受け取っている - <form>の中のth:object="${PersonForm}" で,このフォーム内で参照するオブジェクトを指定 - <input>の中の"*{name}"は,現在参照しているオブジェクトのnameメンバを参照するという意味 -- "${PersonForm.name}" と同じ意味 - <form ... th:object="${フォーム変数}"> ~<input ... th:field=*{属性名}/> -- <input>の中に,id, name, valueを勝手に展開してくれる -- [[フォームバインディング:https://casual-tech-note.hatenablog.com/entry/2018/10/10/224250]]といって, Thymeleaf でフォームを書く時の鉄板らしい - 最下部の<input type="submit" ..> ボタンを押すと入力が<form>の定義にしたがって情報がPOSTされる -- メソッドはPOST /persons -- 送信されるデータは,各inputのnameとvalueのペア - 3.3 のコントローラに渡され,登録後,一覧にリダイレクトされる ** 4.4 受診者更新 (templates/update.html) [#oa7db494] 4.3とほぼ同じなので割愛 ** 完全なコード [#y2d30352] - GitLab: Medical CheckApp -- http://teamserv.cs.kobe-u.ac.jp:443/masa-n/medicalcheckapplication ** 参考文献 [#eb8d78c6] - Thymeleaf チュートリアル -- https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html - Spring Boot で Thymeleaf 使い方メモ -- https://qiita.com/opengl-8080/items/eb3bf3b5301bae398cc2 - [[Spring Boot 2実践入門:簡単なWEBアプリを一から作成チュートリアル:https://www.microstone.info/spring-boot-2%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80%EF%BC%9A%E7%B0%A1%E5%8D%98%E3%81%AAweb%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%B8%80%E3%81%8B%E3%82%89%E4%BD%9C%E6%88%90%E3%83%81%E3%83%A5%E3%83%BC/]] - 【Spring Boot】ModelクラスとModel And Viewクラス -- https://pointsandlines.jp/java/model-and-view - Thymeleafを使用した入力フォームのサンプルコード -- https://qiita.com/rubytomato@github/items/387d46ea34eb92071065 ** コントローラクラステンプレ(by中田) [#ld3fde9d] @Controller class MyController { @GetMapping("/page") public String getSomething( Model model, RedirectAttributes attributes, @ModelAttribute @Validated MyForm form, BindingResult bindingResult){ if(bindingResult.hasErrors()){ attributes.addFlashAttribute("isError", true); attributes.addFlashAttribute("arg", "hoge"); return "redirect:/"; } model.addAttribute("arg", "fuga"); return "next"; } } - @Controller -- ThymeleafのControllerクラスにつけるアノテーション -- コンポーネントスキャンの対象となる - Model -- Viewで使える変数 -- addAttribute : Viewで使える変数を設定する - RedirectAttributes -- リダイレクトに使う -- addAttribute : リダイレクト先に渡す引数の設定 -- addFlashAttribute : リダイレクト先でmodel.addAttributeを実行し、値が消える(flash) - @ModelAttribute -- リクエストボディの内容を受け取る - @Validated -- バリデーションを有効化する - BindingResult -- 直前の引数のバリデーション結果を格納する - return -- "page"だと、page.htmlを返す -- redirect : 指定したURLに移動して読み込み直す
タイムスタンプを変更しない
[[SpringBoot]] * Thymeleaf を使ったView層の開発 [#ee213c4b] 2021-04-05 中田 コントローラクラスのテンプレを追記 2020-06-21 中村 Thymeleafは,SpringBootで推奨されている''テンプレートエンジン''である.~ サーバサイドで画面 (View層)を生成することができる. REST-APIを呼び出して,JavaScriptで動的に画面を書き換えるやり方とは~ アプローチが異なることに注意. - こちらはクライアントサイドで画面を生成するやり方 - VueやAngularもこちらのアプローチになる. ** テンプレートエンジンとは [#d5922d83] アプリケーションは,あらかじめ画面のひな型となるHTML~ (''テンプレート'')を用意しておく. テンプレートは,ところどころ穴あき(変数)のHTMLになっており,~ @ControllerがDTOとテンプレートを指定すれば,ThymeleafがDTOの~ フィールド値をテンプレートの変数に代入して穴埋めし,完全なHTML~ にレンダリングしてブラウザに返却する. ** Thymeleaf の概要 [#xe97162e] &attachref(thymeleaf.png); @Controllerオブジェクト(@RestControllerではない)が処理結果を~ 含んだDTOと画面のひな型となるHTMLテンプレートを指示すれば,~ ThymeleafがDTOの値をテンプレートの指定された場所に組み込んで~ レンダリングする. テンプレートは,Spring Boot プロジェクトのresources/templates/ ~ の下に,Thymeleafの変数を埋め込んだHTML形式で保存する. なお,Spring Boot プロジェクトは,変数を含まないHTML(静的なWeb~ ページ)を持つこともでき,その場合には,resources/static/ の下に~ 保存する. ** 1. 準備 [#l42d9e14] *** build.gradle に依存性を追加 [#v54a471d] build.gradle のdependenciesに以下を追記. implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' [[SpringBoot/依存]]を参考にして,他のライブラリとの前後関係に注意すること. ** 2. 画面設計 [#e005334d] *** 2.1 画面と画面遷移 [#m0ad9a4c] コーディングする前に,まずは画面と画面遷移を大まかでよいので, ノート等にスケッチしておくとよいだろう. 画面 - どんな画面があるか? - それぞれのユースケースごとに画面があるはず 画面遷移 - 画面間の遷移はどうなっているか? 画面遷移の処理の流れを,最初の図を参照しながら,確認しよう &attachref(./thymeleaf.png,50%); + ユーザがブラウザ上のボタンを押す + ControllerにGETまたはPOSTが発行される + ControllerがServiceを経由してビジネスロジックを実行し,結果を得る + Controllerが結果をModelに属性としてセットする.addAttribute() + Controllerがreturnでテンプレートを''文字列''で指定する(例えばhogeとする) + Thymeleafがtemplatesの中からテンプレート''文字列.html'' (hoge.html)を探しだす. + Thymeleafがhoge.htmlにModelの属性値を流しこみ完全なHTMLを生成して,クライアントに返す + ユーザのブラウザ上に画面が現れる *** 2.2 例題:健康診断Webアプリ [#mcfa36c0] 健康診断アプリ([[ソフトウェア工学第4回課題:http://www27.cs.kobe-u.ac.jp/~masa-n/lecture/newse/04/enshuu04.html]])の画面設計を行う - エンティティは[[SpringBoot/JPA]]を参照のこと *** 画面の例 [#k9142c95] &attachref(screens.png); *** 画面遷移の例 [#vf8a3232] &attachref(transition.png); - &color(red){赤字}; は画面に対応するURLパスを表す - 画面間の遷移のラベルは,[ボタン・リンク押下] / [コントローラへのアクション] を表している - redirect:/persons は,アクション終了後にリダイレクトを行う ** 3. @Controller クラスの構成 [#mffd1943] サーバサイドで画面を構成する場合,@RestController クラスではなく,~ @Controller クラスを定義する (両方あっても構わないが,URLパスが~ 重ならないように気を付けること) PersonController.java @Controller public class PersonController { @Autowired PersonManager pm; //サービスクラス (以降メソッド定義) } 各メソッドの定義について,2.2の例題にそって説明する. *** 3.1 受診者リストの表示 (GET /persons) [#td8773fc] 登録されているすべての受診者のデータを画面に表示する + Serviceに問い合わせて,全受診者のリストを取得し personListとする + 画面モデルに,属性plistを定義し,personListを紐づける + テンプレート list.html を呼び出す. @GetMapping("/persons") public String showAllPersons(Model model) { List<PersonDto> personList = pm.getAllPersons(); model.addAttribute("plist", personList); return "list"; } *** 3.2 登録フォームの表示 (GET /persons/register) [#pe4dff18] 登録フォームを表示する + 空のフォームオブジェクトPersonFormを作成する + 画面モデルに,属性PersonFormを定義し,オブジェクトを紐づける + テンプレート register.html を呼び出す. @GetMapping("/persons/register") public String showPersonForm(Model model) { model.addAttribute("PersonForm", new PersonForm()); return "register"; } *** 3.3 受診者情報の登録 (POST /persons) [#a9e3d4cb] 受診者情報をフォームから受け付けてDBに追加する.その後,一覧に戻る + ページからPOSTされた属性と値のペアを,@ModelAttribute のformに受け取る + @Vaidatedでバリデーションする + エンティティに変換して,登録サービスに渡す addPerson() + /personsにリダイレクトし,一覧ページを表示する @PostMapping("/persons") public String addPerson(@ModelAttribute @Validated PersonForm form, BindingResult result, Model model) { pm.addPerson(form.toEntity()); return "redirect:/persons"; } *** 3.4 更新フォームの表示 (GET /persons/{number}/update) [#xda4e9a8] 更新フォームを表示する + パス・パラメータnumberから番号がnumberの受診者pを検索する + 画面モデルに,属性PersonFormを定義し,pを紐づける + テンプレート update.html を呼び出す @GetMapping("/persons/{number}/update") public String showUpdateForm(@PathVariable Long number, Model model) { PersonDto p = pm.getPerson(number); model.addAttribute("PersonForm", p); return "update"; } *** 3.5 受診者情報を更新する (POST /persons/{number}/update) [#t2789157] 画面で更新された受診者情報をフォームから受け付けてDBに追加する.~その後,一覧に戻る - 注意: 更新なのでPUTで呼び出したい所だが,htmlの<form>ではPOST,GETしか指定できないのであえてPOSTで. + ページからPOSTされた属性と値のペアを,@ModelAttribute のformに受け取る + @Vaidatedでバリデーションする + パスパラメータから,受診者番号numberを取得する + formをエンティティに変換して,受診者番号numberとともに更新サービスに渡す updatePerson() + /personsにリダイレクトし,一覧ページを表示する @PostMapping("/persons/{number}/update") public String updatePerson(@ModelAttribute @Validated PersonForm form, @PathVariable Long number, Model model) { pm.updatePerson(number, form.toEntity()); return "redirect:/persons"; } *** 3.6 受診者情報を削除する (GET /persons/{number}/delete) [#p17e6509] 受診者情報を削除する (確認ルーチンは省略している) - 注意: 削除なのでDELETEで呼び出したい所だが,クリックで簡単に削除したいのでGETで. + パスパラメータから,受診者情報numberを取得する + 削除サービスを呼び出す deletePerson + /personsにリダイレクトし,一覧ページを表示する @GetMapping("/persons/{number}/delete") public String deletePerson(@PathVariable Long number, Model model) { pm.deletePerson(number); return "redirect:/persons"; } ** 4. HTMLテンプレートを構成する [#p57092b5] *** 4.1 扉ページ (static/index.html) [#g69a446e] 何の変哲もない,普通の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> *** 4.2 受診者一覧 (templates/list.html) [#u81ade37] 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> *** 解説 [#x1a6b6bd] - ''<html>タグ:'' xmlns:th="http://www.thymeleaf.org" の指定 -- Thymeleafの名前空間 th を定義する.これは必須のおまじない. - ''th: がついている部分''は,Thymeleafが動的に書き換える部分 -- (テンプレート) th:属性名 = "[[Thymeleaf式:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html#%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%80%E3%83%BC%E3%83%89%E5%BC%8F%E6%A7%8B%E6%96%87]]" と書いておくと... -- (実行時) 属性名 = [[Thymeleaf式:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html#%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%80%E3%83%BC%E3%83%89%E5%BC%8F%E6%A7%8B%E6%96%87]]が評価された値 に展開される - ''変数式:'' Contollerから渡された変数の値を取り出す -- <td th:text="${p.name}"></td> と書いておくと (pはControllerから渡されたPersonオブジェクト), -- <td>仲村 匡日出</td> と展開される. - ''インライン式:'' 式をhtmlの中に直接書く -- <td> [ [${p.name}] ]</td> - ''リンク式:'' リンクのコンテキストパスをあんじょうやってくれる -- パス・パラメータ{}は()の中で指定する(C言語のprintf()的な感じ) -- <a th:href="@{/persons/{num}/update(num=${p.number})}">編集</a> と書いておくと -- <a href="/person/1/update">編集</a> と展開される - ''リテラル置換'': |・・・| で囲まれた部分を文字列に展開する - ''三項演算子式:'' 条件に応じて,値を切り替える -- 式1 ? 値1: 値2 -- 式1がtrueの場合,値1に評価される.falseの場合,値2に評価される - ''ブロック'' -- <th:block> </th:block>でテンプレート内のブロックを定義できる -- テンプレートを構造化する用途に使う→(繰り返し,分岐,スコープ制限など) -- 実行時には消える - ''繰り返し'' : リストから一つずつ取り出して表示 -- <タグ th:each="p: ${plist}"> ... </タグ> と書いておくと, -- plistから要素p0,p1,...,pnを一つずつ取り出して, -- <タグ> p_0を展開した内容 </タグ> <タグ> p_1を展開した内容</タグ>...<タグ> p_nを展開した内容</タグ> に展開する - ''条件'': 式が成立した時だけ表示 -- <タグ th:if="式">式がtrueのときだけこの内容が出ます</タグ> -- <タグ th:unless="式">式がfalseのときだけこの内容が出ます</タグ> - ''スイッチ'': 式の値によって分岐 -- <th:block th:switch="式1"> --- <タグ th:case="式2">式1==式2の時だけこの内容が出ます</タグ> ** 4.3 受診者登録 (templates/register.html) [#ma1e1871] 登録フォーム. <!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> *** 解説 [#a467ef52] - Controllerから空のフォームを''PersonForm''という属性名で受け取っている - <form>の中のth:object="${PersonForm}" で,このフォーム内で参照するオブジェクトを指定 - <input>の中の"*{name}"は,現在参照しているオブジェクトのnameメンバを参照するという意味 -- "${PersonForm.name}" と同じ意味 - <form ... th:object="${フォーム変数}"> ~<input ... th:field=*{属性名}/> -- <input>の中に,id, name, valueを勝手に展開してくれる -- [[フォームバインディング:https://casual-tech-note.hatenablog.com/entry/2018/10/10/224250]]といって, Thymeleaf でフォームを書く時の鉄板らしい - 最下部の<input type="submit" ..> ボタンを押すと入力が<form>の定義にしたがって情報がPOSTされる -- メソッドはPOST /persons -- 送信されるデータは,各inputのnameとvalueのペア - 3.3 のコントローラに渡され,登録後,一覧にリダイレクトされる ** 4.4 受診者更新 (templates/update.html) [#oa7db494] 4.3とほぼ同じなので割愛 ** 完全なコード [#y2d30352] - GitLab: Medical CheckApp -- http://teamserv.cs.kobe-u.ac.jp:443/masa-n/medicalcheckapplication ** 参考文献 [#eb8d78c6] - Thymeleaf チュートリアル -- https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf_ja.html - Spring Boot で Thymeleaf 使い方メモ -- https://qiita.com/opengl-8080/items/eb3bf3b5301bae398cc2 - [[Spring Boot 2実践入門:簡単なWEBアプリを一から作成チュートリアル:https://www.microstone.info/spring-boot-2%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80%EF%BC%9A%E7%B0%A1%E5%8D%98%E3%81%AAweb%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%B8%80%E3%81%8B%E3%82%89%E4%BD%9C%E6%88%90%E3%83%81%E3%83%A5%E3%83%BC/]] - 【Spring Boot】ModelクラスとModel And Viewクラス -- https://pointsandlines.jp/java/model-and-view - Thymeleafを使用した入力フォームのサンプルコード -- https://qiita.com/rubytomato@github/items/387d46ea34eb92071065 ** コントローラクラステンプレ(by中田) [#ld3fde9d] @Controller class MyController { @GetMapping("/page") public String getSomething( Model model, RedirectAttributes attributes, @ModelAttribute @Validated MyForm form, BindingResult bindingResult){ if(bindingResult.hasErrors()){ attributes.addFlashAttribute("isError", true); attributes.addFlashAttribute("arg", "hoge"); return "redirect:/"; } model.addAttribute("arg", "fuga"); return "next"; } } - @Controller -- ThymeleafのControllerクラスにつけるアノテーション -- コンポーネントスキャンの対象となる - Model -- Viewで使える変数 -- addAttribute : Viewで使える変数を設定する - RedirectAttributes -- リダイレクトに使う -- addAttribute : リダイレクト先に渡す引数の設定 -- addFlashAttribute : リダイレクト先でmodel.addAttributeを実行し、値が消える(flash) - @ModelAttribute -- リクエストボディの内容を受け取る - @Validated -- バリデーションを有効化する - BindingResult -- 直前の引数のバリデーション結果を格納する - return -- "page"だと、page.htmlを返す -- redirect : 指定したURLに移動して読み込み直す
テキスト整形のルールを表示する
添付ファイル:
thymeleaf.png
251件
[
詳細
]
screens.png
157件
[
詳細
]
transition.png
181件
[
詳細
]
medicalcheckapplication-master.zip
93件
[
詳細
]