今回は、入力画面・確認画面・完了画面の3画面を含み、HTMLオブジェクトとしてテキストボックス・セレクトボックス・ラジオボタン・チェックボックスを含むWEBアプリケーションを作成してみたので、そのサンプルプログラムを共有する。
様々なサイト上で、テキストとボタンのみを含むSpring BootのWEBアプリケーションのサンプルプログラムは多く見かけたが、開発現場でよく見かける、セレクトボックス・ラジオボタン・チェックボックスも含むようなプログラムはなかなか見つからなかったので、今回、いろいろなフォームオブジェクトを含むサンプルプログラムを作成してみることにした。
前提条件
以下の記事のSpring BootのWEB画面用アプリが作成済であること。
また、以下の記事のlombokの設定が完了していること。
やってみたこと
完成した画面イメージの共有
今回は、サンプルプログラムの内容をより理解しやすくするために、先に完成した画面イメージの共有を行う。
まずは、Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスすることで、以下の入力画面を表示する
入力画面の値を、以下のように変更し、「確認」ボタンを押下し、確認画面に遷移
確認画面では、入力画面の値が以下のように表示される。この状態で「送信」ボタンを押下し、完了画面に遷移
なお、「戻る」ボタンを押下した場合は、入力画面に戻る。
以下のような完了画面が表示される。「閉じる」ボタンを押下すると、この画面が閉じる
サンプルプログラムの作成
上記のうち、「DemoForm.java」の内容は以下の通り。こちらは画面間で持ち回す項目のformオブジェクトを定義している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | package com.example.demo; import lombok.Getter; import lombok.Setter; import java.util.LinkedHashMap; import java.util.Map; /** * Formオブジェクトのクラス */ public class DemoForm { /** 名前 */ @Getter @Setter private String name; /** 生年月日_年 */ @Getter @Setter private String birthYear; /** 生年月日_月 */ @Getter @Setter private String birthMonth; /** 生年月日_日 */ @Getter @Setter private String birthDay; /** 性別 */ @Getter @Setter private String sex; /** 確認チェック */ @Getter @Setter private String checked; /** 生年月日_月の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; } } |
この中で、各項目のSetter/Getterメソッドは、lombokを利用している。また、セレクトボックス・ラジオボタンで表示するリストについては、getXXXItemsメソッドにより定義していて、それらはMapオブジェクト(キー項目, 値)のリストにより返却される仕組みになっている。
コントローラクラスである「DemoController.java」の内容は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | package com.example.demo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.support.SessionStatus; /** * コントローラクラス * 「@SessionAttributes(types = DemoForm.class)」により、 * 生成したFormオブジェクトをセッションとしてもたせている */ @Controller @SessionAttributes(types = DemoForm.class) public class DemoController { /** * Formオブジェクトを初期化して返却する * @return Formオブジェクト */ @ModelAttribute("demoForm") public DemoForm createDemoForm(){ DemoForm demoForm = new DemoForm(); //名前・性別の初期値を設定する demoForm.setName("テスト 名前"); demoForm.setSex("1"); return demoForm; } /** * 入力画面に遷移する * @param demoForm Formオブジェクト * @return 入力画面へのパス */ @GetMapping("/") public String index(DemoForm demoForm){ return "input"; } /** * 確認画面に遷移する * @param demoForm Formオブジェクト * @return 確認画面へのパス */ @PostMapping("/confirm") public String confirm(DemoForm demoForm){ return "confirm"; } /** * 完了画面へのリダイレクトパスに遷移する * @return 完了画面へのリダイレクトパス */ @PostMapping("/send") public String send(){ return "redirect:/complete"; } /** * 完了画面に遷移する * @param sessionStatus セッションステータス * @return 完了画面 */ @GetMapping("/complete") public String complete(SessionStatus sessionStatus){ //セッションオブジェクトを破棄 sessionStatus.setComplete(); return "complete"; } } |
ここでは、各画面に遷移する処理を定義しているが、それに加え、画面で持ち回すformオブジェクト(demoForm)を生成し、セッションとしてもたせる機能ももっている。createDemoFormメソッドでformオブジェクトを生成し、ここで初期値の設定も行い、コントローラクラスの先頭のアノテーション「@SessionAttributes(types = DemoForm.class)」により、セッションによる持ち回しができるようにしている。
さらに、createDemoFormメソッドに「@ModelAttribute(“demoForm”)」が指定してあり、これによって、画面を定義するhtmlソースから、demoFormを参照できるようになっている。
また、HTTP通信のGETリクエスト/POSTリクエストの使い分けを、@GetMapping、@PostMappingというアノテーションで実現している。詳細は以下のサイトを参照のこと。
https://dkssksk.com/springbootmapping/
その他、ボタンの二度押し防止のため、sendメソッド内ではPRGパターンを利用している。PRGパターンを利用したサンプルプログラムについては、以下のサイトを参照のこと。
入力画面である「input.html」の内容は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>入力画面</title> </head> <body> <p>下記必要事項を記載の上、「確認」ボタンを押下してください。</p><br/> <form method="post" th:action="@{/confirm}" th:object="${demoForm}"> 名前: <input type="text" th:value="*{name}" th:field="*{name}" /> <br/> 生年月日: <input type="text" th:value="*{birthYear}" size="4" maxlength="4" th:field="*{birthYear}" />年 <select th:field="*{birthMonth}"> <option value="">---</option> <option th:each="item : *{getMonthItems()}" th:value="${item.key}" th:text="${item.value}"/> </select>月 <select th:field="*{birthDay}"> <option value="">---</option> <option th:each="item : *{getDayItems()}" th:value="${item.key}" th:text="${item.value}"/> </select>日 <br/> 性別: <for th:each="item : *{getSexItems()}"> <input type="radio" th:value="${item.key}" th:text="${item.value}" th:field="*{sex}" /> </for> <br/> 入力確認: <input type="checkbox" th:value="確認済" th:field="*{checked}" /> <br/><br/> <input type="submit" value="確認" /> </form> </body> </html> |
ここでは、formタグの「th:object=”${demoForm}”」を指定することで、DemoController.javaで生成したフォームオブジェクト「demoForm」を参照している。また、フォームの各フィールドには「th:field=”*{XXX}”」と指定することで、demoFormオブジェクトのXXXフィールドへのgetterメソッド・setterメソッドや、XXXメソッドにアクセスしている。
プルダウンオブジェクトを「select~optionタグ」で、ラジオボタンを「input」タグで「type=”radio”」を指定することで生成している。さらに、プルダウン・ラジオボタンの各項目は、「th:each」タグ内で、「DemoForm.java」の「getXXXItems」メソッドを呼び出すことで生成している。
確認画面である「confirm.html」の内容は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <!DOCTYPE html> <html lang="ja" 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}"> <p th:text="'名前: ' + *{name}">ここに名前が表示されます</p> <p th:text="'生年月日: ' + *{birthYear} + '年' + *{getMonthItems().get('__*{birthMonth}__')} + '月' + *{getDayItems().get('__*{birthDay}__')} + '日'"> ここに生年月日が表示されます </p> <p th:text="'性別: ' + *{getSexItems().get('__*{sex}__')}"> ここに性別が表示されます </p> <p th:text="'確認チェック: ' + *{checked}"> ここに確認チェック内容が表示されます </p> <input type="submit" value="送信" /> <input type="button" value="戻る" onclick="history.back();" /> </form> </body> </html> |
ここでは、例えば「*{getSexItems().get(‘__*{sex}__’)}」という記述をすることで、性別の表示文言「女」を表示している。getメソッド内の引数「__*{sex}__」を先に実行し(これをプリプロセッシング機能という)、性別Mapオブジェクトのキー項目である「2」が取得される。その後、「*{getSexItems().get(‘2’)}」を実行し、「女」が表示されるようになっている。生年月日_月、生年月日_日も、性別と同じ仕組みで表示している。
完了画面である「complete.html」の内容は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>完了画面</title> </head> <body> お申し込みが完了しました。<br/><br/> <input type="button" value="閉じる" onclick="window.close();" /> </body> </html> |
こちらは、完了メッセージと「閉じる」ボタンを表示している。
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-web-somescreen/demo
また、その他のフォームオブジェクトを利用した場合の実装方法は、下記サイトが参考になる。
https://qiita.com/rubytomato@github/items/8da1bb19537bbfc9c2ea
要点まとめ
- 画面間で持ち回す各項目のformオブジェクトや、セレクトボックス・ラジオボタンで表示するリストは、Formクラス内で定義する。
- formオブジェクトはコントローラクラスで生成し、「@ModelAttribute」アノテーションでHTMLファイルとの紐付けを行い、「@SessionAttributes」アノテーションでセッションとしてもたせされる。
- HTMLファイルでのformオブジェクトの参照は「th:object」タグで行い、formオブジェクト内のメソッドへのアクセスは「th:field」タグで行う。
- 式の一部に含まれる「__(変数)__」はプリプロセッシング機能で、式の中で先に実行される。