前回の要点
- http://start.spring.ioでプロジェクトを作成する。
- 処理の流れ
- テンプレートとコントローラを作る。
- テンプレート
- HTMLで記述する。ただし単独で現れるタグは後ろに「/」をつける。
- コントローラから送られてきた値を表示するところで、th:text属性を使う。
- コントローラ
- クラスに@Controllerアノテーションをつける。
- クラスとメソッドにつける@RequestMappingアノテーションで担当するURLを指定する。
- メソッドの返り値で利用するテンプレートを指定する。
- テンプレートに値を送り込みたい場合は、メソッドにModelMap型の引数を加え、そのaddAttributeメソッドで名前と値を指定する。
- その他
- ポート番号を変える: resourcesにあるapplication.propertiesに「server.port = 18080」などと記述する。
ドキュメント
Formの作成と表示
おみくじアプリを作ってみよう。
処理のシーケンスは次のようになる。
名前を入力する(フォームの利用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>おみくじ</title> </head> <body> <form action="omikuji_output" method="post"> 名前を入力してください <input name="name" /><br/> <button type="submit">おみくじを引く</button> </form> </body> </html> |
- formタグ: どこからどこまでがフォームかを表す。action属性でどこに送るか指定する。method属性でどのような形で送るかを指定する。
- inputタグ: name属性で区別のための名前をつける。この例ではその名前にnameとつけて紛らわしいので注意すること。
- buttonタグ: type属性をsubmitとすることで、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 |
package net.teachingprogramming.webapp; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; /** * 第4回で使うコントローラ */ @Controller @RequestMapping("/lecture04") public class Lecture04Controller { /** * おみくじ: 名前の入力 */ @RequestMapping("/omikuji_input") public String omikujiInput() { return "lecture04/omikuji_input"; } /** * おみくじ: 結果の出力 */ @RequestMapping("/omikuji_output") public String omikujiOutput(ModelMap modelMap, @RequestParam("name") String name) { String result = "吉"; if (Math.random() < 0.5) { result = "大吉"; } modelMap.addAttribute("result", result); modelMap.addAttribute("name", name); return "lecture04/omikuji_output"; } } |
- 18〜21行目:「/lecture04/omikuji_input」にアクセスがあると、「lecture04/omikuji_input」がブラウザに送られる。
- 26〜34行目:「/lecture04/omikuji_output」にアクセスがあると、「lecture04/omikuji_output」がブラウザに送られる。
- 27行目:「フォームから送られてくるパラメータを受け取るための引数」を指定する為に@RequestParamアノテーションをつける。
- 28〜31行目: String型の変数を乱数によって「吉」か「大吉」にする。
- 32行目: 「result」という名前で変数resultをテンプレートに送る。
- 32行目: 「name」という名前で変数nameをテンプレートに送る。
結果の表示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>おみくじ</title> </head> <body> <p> <span th:text="${name}">nameの値</span>さんの今日の運勢は<span th:text="${result}">resultの値</span>です。 </p> </body> </html> |
- コントローラからは「name」と「result」という名前で値が送られてきている。それぞれ、th:text属性で表示する。
例1: 掲示板
テンプレート
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 |
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>掲示板</title> </head> <body> <ul> <li th:each="comment:${commentList}"> <th:block th:text="${comment}">コメント</th:block> </li> <!-- 上とほぼ同じ <li th:each="comment:${commentList}" th:text="${comment}">コメント</li> --> </ul> <hr /> <form method="post"> <input name="comment" /> <button type="submit">書き込む</button> </form> </body> </html> |
- 最初の表示(get)とコメントを書き込む時(post)の両方でこのテンプレートを使う。
- formのaction属性は省略すると、同じURLにアクセスすることになる。
- リストから一つづつ取り出すのは、th:each属性を使う。コロンの左側でつけた名前でアクセスできる(スコープは、属性をつけた要素の内側)。注意しなければならないのは、繰り返し出力されるのは、th:each属性がついた要素(タグ)だということ。例えば、commentListに3つの文字列が入っていたとしたら、li要素が3つ出力される。
- タグ無しで値を出力するのはth:blockタグを使う。
コントローラ
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
/** * 掲示板: コメントを保存しておくフィールド */ private List<String> commentList = new ArrayList<>(); // private ArrayList<String> commentList = new ArrayList<String>(); // ListとArrayListの関係がよくわからない人向け /** * 掲示板: get */ @GetMapping("bbs") public String bbsGet(ModelMap modelMap) { modelMap.addAttribute("commentList", commentList); return "lecture04/bbs"; } /** * 掲示板: post */ @PostMapping("bbs") public String bbsGet(ModelMap modelMap, @RequestParam("comment") String comment) { commentList.add(comment); modelMap.addAttribute("commentList", commentList); return "lecture04/bbs"; } |
- @RequestMappingはgetとpostの両方で実行されるが、@GetMappingと@PostMappingを使うことで、同じURLでgetとpostの処理を別のメソッドに分けて書くことができる。
- コントローラ全体
例2: じゃんけん
テンプレート
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>じゃんけん</title> </head> <body> <p th:if="${result != null}" th:text="${result}"> 結果がある場合表示する </p> <form method="post"> <input type="radio" name="janken" value="g" checked="checked"/> グー<br /> <input type="radio" name="janken" value="c" /> チョキ<br /> <input type="radio" name="janken" value="p" /> パー<br /> <button type="submit">じゃんけんする</button> </form> </body> </html> |
- 最初の表示(get)とじゃんけんをした時(post)の両方でこのテンプレートを使う。
- th:ifで条件が真(true)の時だけ要素を表示させることができる。ここでは、コントローラからresultが送られてきた時(つまりresultがnullでない)にそれを表示している。
- input要素のtype属性は「radio」。checked=”checked”をつけた要素がデフォルトで選択される。
- name属性は全て同じにする(nameを同じにすると同一グループとなり排他的選択ができる)。
- 選択されたボタンのvalue属性がコントローラに送られる。
コントローラ
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
/** * じゃんけん: get */ @GetMapping("janken") public String janken() { return "lecture04/janken"; } /** * じゃんけん: post */ @PostMapping("janken") public String janken(ModelMap modelMap, @RequestParam("janken") String janken) { StringBuilder resultBuilder = new StringBuilder(); resultBuilder.append("あなたは"); if (janken.equals("g")) { resultBuilder.append("グー"); } else if (janken.equals("c")) { resultBuilder.append("チョキ"); } else { resultBuilder.append("パー"); } resultBuilder.append("を出して"); double random = Math.random(); if (random < 0.333) { resultBuilder.append("勝ちました。"); } else if (random < 0.666) { resultBuilder.append("負けました。"); } else { resultBuilder.append("引き分けました。"); } modelMap.addAttribute("result", resultBuilder.toString()); return "lecture04/janken"; } |
- getとpostで同じメソッド名となっているが、これをオーバーロードという(教科書P.132)。
- StringBuilderは文字列を繋げる時に使える(教科書P.82)。
- 律儀にコンピュータの手を決定して結果を出力してもいいが、ここでは選択した手に関わらずランダムで結果を出力している。
- コントローラ全体
例3: アンケート
テンプレート
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 |
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>チェックボックス</title> </head> <body> <p> 好きなプログラミング言語はどれですか? </p> <form method="get"> Java <input type="checkbox" name="java" /><br /> Kotlin <input type="checkbox" name="kotlin" /><br /> Scala <input type="checkbox" name="scala" /><br /> <button type="submit">送信</button> </form> <p th:if="${result != null}" th:text="${result}"> チェックボックスを送信した結果。 </p> </body> </html> |
- チェックボックスはname属性で区別する。
コントローラ
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
/** * アンケート */ @RequestMapping("questionnaire") public String questionnaire(ModelMap modelMap, @RequestParam(value = "java", required = false) boolean java, @RequestParam(value = "kotlin", required = false) boolean kotlin, @RequestParam(value = "scala", required = false) boolean scala) { StringBuilder resultBuilder = new StringBuilder(); if (java) { resultBuilder.append("Javaがチェックされました。"); } if (kotlin) { resultBuilder.append("Kotlinがチェックされました。"); } if (scala) { resultBuilder.append("Scalaがチェックされました。"); } modelMap.addAttribute("result", resultBuilder.toString()); return "lecture04/questionnaire"; } |
- チェックボックスは、チェックをしたときにしかパラメータが送信されない。そのため、デフォルトの@RequestParamではエラーになる。
- パラメータが送信されなくてもエラーにならないようにするには、「required = false」とする。
- チェックされているときはtrue、チェックされてない時(パラメータが無い時)は、falseになる。
- コントローラ全体