いよいよ社員の新規登録を作ります。新規登録は「入力 → 確認 → 完了」と複数の画面にまたがるため、入力内容を覚えておく「セッション」という仕組みを使います。この章では、入力画面と、入力した内容を表示する確認画面までを作ります。
この章で学ぶこと
- セッションとは何か、なぜ必要なのか
- 画面の入力内容を受け取るフォームクラスの作り方
-
@SessionAttributesで入力内容をセッションに保存する方法 - Thymeleaf で入力画面(テキスト・ラジオボタン・プルダウン・日付)を作る方法
- セッションに保存した入力内容を、確認画面で表示する方法
10.1 新規登録の全体像
新規登録は、次のように3つの画面で進みます。確認画面からは、入力画面に戻って内容を直すこともできます。
この章では、入力画面 と、画面の間で入力内容を覚えておく セッション の準備、そして入力した内容を表示する 確認画面 までを作ります。確認画面の「戻る」「登録」のボタンと、完了画面は第11章で作ります。
10.2 セッションとは
HTTP はステートレス(前のリクエストを覚えていない)
第1章で学んだように、ブラウザとサーバーは HTTP でやり取りします。HTTP は、1回1回のリクエストを別々のものとして扱い、前のリクエストの内容を覚えていません。この性質を ステートレス(状態を持たない)といいます。
たとえば、入力画面で「確認」を押したときのリクエストと、確認画面で「登録」を押したときのリクエストは、サーバーから見ると別々のリクエストです。1つ目のリクエストで送った氏名や年齢は、2つ目のリクエストのときには、サーバーには分かりません。新規登録では、入力画面で入力した内容を、確認画面や完了画面(登録処理)でも使いたいので、これでは困ります。
セッションとセッションID
そこで使うのが セッション です。セッションは、サーバーの中にある、ブラウザごとの専用の保管場所 です。
サーバーは、初めてアクセスしてきたブラウザに、セッションID という番号を渡します。ブラウザはセッションIDを Cookie に保存し、以降のリクエストで毎回セッションIDを送ります。サーバーはセッションIDを見て、どの保管場所から入力内容を取り出せばよいかを判断します。
- セッションIDは、ブラウザの Cookie(クッキー) に
JSESSIONIDという名前で保存されます - 保管場所はブラウザごとに1つです。別のブラウザ(別のパソコン)からのアクセスは、別の保管場所になります
- しばらく操作しないと(標準では30分)、保管場所は自動的に削除されます
入力内容を画面の hidden 項目(画面に表示されない入力欄)に入れて持ち回る方法もありますが、この教材ではセッションを使います。
10.3 application.properties に設定を追加する
セッションを使い始める前に、src/main/resources/application.properties の最後に1行追加します。
spring.application.name=employee-app
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/XEPDB1
spring.datasource.username=empapp
spring.datasource.password=empapp
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.open-in-view=false
spring.thymeleaf.cache=false
server.servlet.session.tracking-modes=cookie
この設定は「セッションIDは Cookie だけでやり取りする」という意味です。この設定がないと、セッションを使い始めた最初の画面で、URL の後ろに ;jsessionid=A1B2… のようなセッションIDが付いてしまうことがあります。URL にセッションIDが見えるのは見た目がよくないうえ、URL をコピーして他人に送るとセッションまで共有されてしまうおそれがあるため、Cookie だけを使うようにしておきます。
【知識メモ】application.properties のコメントについて
第4章で各行に日本語のコメント(# で始まる行)を付けた人は、そのままでかまいません。上のコードではコメントを省略しています。
10.4 フォームクラスを作る
入力画面で入力された内容を受け取るためのクラスを作ります。このように、画面の入力項目を受け取るクラスを フォームクラス といいます。
作成手順
-
src/main/javaのcom.example.employeeappパッケージを右クリックし、新規 → パッケージ を選びます。名前にcom.example.employeeapp.formと入力して 完了 を押します。 - 作成した
com.example.employeeapp.formパッケージを右クリックし、新規 → クラス を選びます。名前にRegisterFormと入力して 完了 を押します。 - 次のコードを入力します。getter と setter は、第6章と同じように ソース → getter および setter の生成 で作ると楽です。
package com.example.employeeapp.form;
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
public class RegisterForm implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Integer gender;
private Integer age;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthDate;
private Integer deptId;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
ポイント
Entity とは別にフォームクラスを作る理由
「Employee(Entity)をそのまま使えばよいのでは?」と思うかもしれません。しかし、画面の入力項目と、データベースのテーブルは、必ずしも同じ形ではありません。
| 項目 | フォームクラス(RegisterForm) | Entity(Employee) |
|---|---|---|
| 部署 |
Integer deptId(プルダウンで選んだ部署ID) |
Department department(部署のオブジェクト) |
| 用途 | 画面との受け渡し | データベースとの受け渡し |
役割ごとにクラスを分けておくと、画面を変えたときにデータベース側に影響が出にくくなります。第13章で追加する入力チェックも、フォームクラスに書きます。
implements Serializable
このフォームはセッションに保存します。本番の環境では、複数のサーバーでセッションを共有するために、セッションの中身をデータベースなどに保存したり、ほかのサーバーへ送ったりすることがあります。そのときオブジェクトを保存できる形に変換するので、Serializable(保存できる形に変換できる、という印)を実装しておきます。書き忘れると、NotSerializableException になります。
serialVersionUID は、保存したときと読み戻すときで、同じクラスかどうかを確かめるための番号です。書いておくと、フィールドを足すなどしてクラスを変えても、前に保存した内容を読み戻せます。
@DateTimeFormat
画面から送られてくる生年月日は、2001-06-15 のような文字列です。@DateTimeFormat(pattern = "yyyy-MM-dd") を付けておくと、Spring がこの形式の文字列を Date に自動で変換してくれます。反対に、Date を画面に表示するときも、この形式の文字列に変換してくれます。
10.5 新規登録の Controller を作る
新規登録の処理をまとめる Controller を作ります。一覧・削除の EmployeeListController とは別のクラスにします。
-
com.example.employeeapp.controllerパッケージを右クリックし、新規 → クラス を選びます。 - 名前に
EmployeeRegisterControllerと入力して 完了 を押し、次のコードを入力します。
package com.example.employeeapp.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import com.example.employeeapp.entity.Department;
import com.example.employeeapp.form.RegisterForm;
import com.example.employeeapp.repository.DepartmentRepository;
import com.example.employeeapp.repository.EmployeeRepository;
@Controller
@SessionAttributes("registerForm")
public class EmployeeRegisterController {
@Autowired
private EmployeeRepository employeeRepository;
@Autowired
private DepartmentRepository departmentRepository;
/** セッションに RegisterForm オブジェクトがないとき呼ばれる
* @return 空の RegisterForm オブジェクト */
@ModelAttribute
public RegisterForm createForm() {
return new RegisterForm();
}
/** 社員登録の入力画面を表示
* @param model 表示する入力フォームと部署一覧
* @return 社員登録の入力画面 */
@GetMapping("/employees/register")
public String input(Model model) {
model.addAttribute("registerForm", new RegisterForm());
List<Department> departments = departmentRepository.findAllByOrderByDeptIdAsc();
model.addAttribute("departments", departments);
return "employee/register/input";
}
/** 社員登録の確認画面を表示
* @param registerForm 入力された社員情報
* @param model 表示する部署情報
* @return 社員登録の確認画面 */
@PostMapping("/employees/register/confirm")
public String confirm(@ModelAttribute RegisterForm registerForm, Model model) {
Department department = departmentRepository.findByDeptId(registerForm.getDeptId());
model.addAttribute("department", department);
return "employee/register/confirm";
}
}
【知識メモ】employeeRepository はまだ使いません
employeeRepository は、第11章で社員を登録するときに使います。この章の時点では使っていないため、Eclipse が黄色の警告(「使用されていません」など)を表示することがありますが、問題ありません。
コードの解説
| 記述 | 意味 |
|---|---|
@GetMapping("/employees/register") |
GET /employees/register(一覧画面の「新規登録」ボタン)で input() が呼ばれる。URL は、メソッドごとに最後まで書く |
@SessionAttributes("registerForm") |
Model に registerForm という名前で入れたオブジェクトを、自動でセッションに保存 する。次のリクエストでは、自動でセッションから取り出して Model に入れてくれる |
@ModelAttribute を付けた createForm()
|
セッションに registerForm がまだないときに呼ばれ、空のフォームを用意する。名前は、戻り値の クラス名(RegisterForm)の先頭を小文字にした registerForm になるので、@ModelAttribute("registerForm") のように名前を書く必要はない |
input() |
入力画面を表示する。空のフォームと、プルダウン用の部署一覧を Model に入れる |
@PostMapping("/employees/register/confirm") |
入力画面の「確認」ボタン(POST /employees/register/confirm)で confirm() が呼ばれる |
confirm() |
入力内容を受け取って確認画面を表示する。部署名を表示するため、部署IDから部署を検索して Model に入れる。引数の @ModelAttribute は 10.10 で解説する |
findAllByOrderByDeptIdAsc() と model.addAttribute("departments", departments)
|
部署のプルダウンに表示する部署の一覧を取り出して、Model に入れる。部署のプルダウンがある画面を表示するたびに、この2行を書く |
@SessionAttributes のしくみ
@SessionAttributes を使うと、セッションへの保存と取り出しを自分で書く必要がありません。
-
input()で Model にregisterFormを入れる - 処理が終わると、Spring が
registerFormを セッションに保存 する - 次のリクエスト(「確認」ボタンを押したときなど)で、Spring がセッションから
registerFormを 取り出して くれる
input() で毎回 new RegisterForm() を入れ直す理由
セッションには、前回の入力内容が残っていることがあります(途中で一覧に戻った場合など)。「新規登録」ボタンを押したときは、いつも空の状態から入力を始めたい ので、input() では新しい空のフォームで上書きしています。
10.6 一覧画面に「新規登録」ボタンを付ける
templates/employee/list.html を開き、見出しの下に「新規登録」ボタンを追加します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>社員一覧</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
<h1>社員一覧</h1>
<p><a class="button" th:href="@{/employees/register}">新規登録</a></p>
<table>
<tr>
<th>社員ID</th>
<th>氏名</th>
<th>性別</th>
<th>年齢</th>
<th>生年月日</th>
<th>部署</th>
<th>住所</th>
<th>操作</th>
</tr>
<tr th:each="employee : ${employees}">
<td th:text="${employee.empId}"></td>
<td th:text="${employee.name}"></td>
<td th:switch="${employee.gender}">
<span th:case="1">男</span>
<span th:case="2">女</span>
</td>
<td th:text="${employee.age}"></td>
<td th:text="${#dates.format(employee.birthDate, 'yyyy/MM/dd')}"></td>
<td th:text="${employee.department.deptName}"></td>
<td th:text="${employee.address}"></td>
<td>
<form class="inline" th:action="@{/employees/{empId}/delete(empId=${employee.empId})}" method="post">
<button type="submit">削除</button>
</form>
</td>
</tr>
</table>
</body>
</html>
入力画面を表示するだけで、データは変えないので、フォームではなく リンク(<a>)で作っています。リンクをクリックすると、GET でリクエストが送られます(第1章の「見るだけなら GET」)。class="button" は、第8章で作った app.css の a.button で、リンクをボタンの見た目にするための指定です。
10.7 入力画面のテンプレートを作る
-
src/main/resources/templates/employeeフォルダーを右クリックし、新規 → フォルダー を選びます。フォルダー名にregisterと入力して 完了 を押します。 - 作成した
registerフォルダーを右クリックし、新規 → ファイル を選びます。ファイル名にinput.htmlと入力して 完了 を押します。 - 次のコードを入力します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>社員新規登録</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
<h1>社員新規登録</h1>
<form th:action="@{/employees/register/confirm}" th:object="${registerForm}" method="post">
<table>
<tr>
<th>氏名</th>
<td>
<input type="text" th:field="*{name}">
</td>
</tr>
<tr>
<th>性別</th>
<td>
<label><input type="radio" th:field="*{gender}" value="1"> 男</label>
<label><input type="radio" th:field="*{gender}" value="2"> 女</label>
</td>
</tr>
<tr>
<th>年齢</th>
<td>
<input type="text" th:field="*{age}">
</td>
</tr>
<tr>
<th>生年月日</th>
<td>
<input type="date" th:field="*{birthDate}">
</td>
</tr>
<tr>
<th>部署</th>
<td>
<select th:field="*{deptId}">
<option value="">選択してください</option>
<option th:each="department : ${departments}"
th:value="${department.deptId}"
th:text="${department.deptName}"></option>
</select>
</td>
</tr>
<tr>
<th>住所(都道府県)</th>
<td>
<input type="text" th:field="*{address}">
</td>
</tr>
</table>
<button type="submit">確認</button>
</form>
<p><a th:href="@{/employees}">一覧へ戻る</a></p>
</body>
</html>
10.8 確認画面のテンプレートを作る
「確認」ボタンを押したときに表示する確認画面を作ります。入力画面と同じ register フォルダーに作ります。
-
src/main/resources/templates/employee/registerフォルダーを右クリックし、新規 → ファイル を選びます。ファイル名にconfirm.htmlと入力して 完了 を押します。 - 次のコードを入力します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>社員新規登録(確認)</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
<h1>社員新規登録(確認)</h1>
<p>次の内容で登録します。よろしければ「登録」ボタンを押してください。</p>
<table th:object="${registerForm}">
<tr><th>氏名</th><td th:text="*{name}"></td></tr>
<tr><th>性別</th><td th:switch="*{gender}"><span th:case="1">男</span><span th:case="2">女</span></td></tr>
<tr><th>年齢</th><td th:text="*{age}"></td></tr>
<tr><th>生年月日</th><td th:text="*{#dates.format(birthDate, 'yyyy/MM/dd')}"></td></tr>
<tr><th>部署</th><td th:text="${department?.deptName}"></td></tr>
<tr><th>住所(都道府県)</th><td th:text="*{address}"></td></tr>
</table>
</body>
</html>
「戻る」「登録」のボタンは、第11章で付けます。
10.9 動作確認
- すべてのファイルを保存します(Ctrl + Shift + S で、開いているファイルをまとめて保存できます)。
-
application.propertiesを変更したので、アプリをいったん停止してから起動し直します。 - Chrome で
http://localhost:8080/employeesを開きます。一覧の上に「新規登録」ボタンが表示されます。
- 「新規登録」ボタンを押します。空の入力画面が表示されれば成功です。部署のプルダウンを開くと、第3章で登録した部署が並んでいます。
-
試しに値を入力してみましょう。生年月日の欄は、右端のカレンダーのアイコンを押すと、カレンダーから日付を選べます。

図10-5 値を入力した入力画面 -
「確認」ボタンを押します。入力した内容が表示された確認画面になれば成功です。
性別は「2」ではなく「女」、部署は「3」ではなく「開発部」と表示されていることを確認してください。
この章ではここまでです。確認画面の「戻る」「登録」のボタンと、完了画面は第11章で作ります。
10.10 解説:入力画面と確認画面のしくみ
@ModelAttribute でセッションのフォームを受け取る
confirm メソッドの引数を見てみましょう。
public String confirm(@ModelAttribute RegisterForm registerForm, Model model) {
@ModelAttribute を付けた引数には、Spring が次のことをしてから値を渡してくれます。
- セッションに保存されている
registerForm(入力画面で使ったフォーム)を取り出す - 入力画面から送られてきた値(氏名、性別、年齢…)を、そのフォームのフィールドに詰める
取り出すフォームの名前は、引数の クラス名(RegisterForm)の先頭を小文字にした registerForm になります。@SessionAttributes("registerForm") の名前と同じなので、@ModelAttribute("registerForm") のように名前を書く必要はありません。
つまり、registerForm には入力画面で入力した内容がすべて入っています。そして @SessionAttributes("registerForm") が付いているので、このフォームはリクエストが終わったあともセッションに保存され続けます。第11章では、このフォームを「戻る」と「登録」でも使います。
th:object と *{…}
<form> に th:object="${registerForm}" と書くと、そのフォームの中では *{name} のように書くだけで、registerForm の項目を指せます。*{name} は ${registerForm.name} を短く書いたものです。
th:field
th:field="*{name}" と書くと、Thymeleaf が次の属性を自動で作ってくれます。
| 自動で作られる属性 | 内容 |
|---|---|
id="name" |
項目名と同じ id |
name="name" |
送信するときの名前。Spring はこの名前で、フォームクラスの setName() に値を入れる |
value="…" |
フォームに今入っている値。第11章で確認画面から戻ったときに、入力した値が表示されるのはこのおかげ |
実際にどんな HTML になったかは、入力画面で右クリック → ページのソースを表示 で確認できます。
<input type="text" id="name" name="name" value="">
ラジオボタン
ラジオボタンでは、th:field="*{gender}" と一緒に value="1"・value="2" を書きます。選んだほうの value が gender に入ります。フォームの gender に値が入っているときは、その値のラジオボタンが自動で選択された状態(checked)になります。
<label> で囲んでいるのは、「男」「女」の文字をクリックしてもラジオボタンを選べるようにするためです。
プルダウン(select)
-
th:each="department : ${departments}"で、部署の数だけ<option>を作ります(第8章の一覧の行と同じ考え方です) -
th:valueに部署ID、th:textに部署名を入れるので、画面には部署名が表示され、送信されるのは部署ID です - 最初の
<option value="">選択してください</option>は、まだ何も選んでいない状態を表します
日付の入力欄(type="date")
<input type="date"> にすると、Chrome ではカレンダーから日付を選べる入力欄になります。画面には 2001/06/15 のように表示されますが、送信されるときは 2001-06-15 の形式になります。この形式が、フォームクラスの @DateTimeFormat(pattern = "yyyy-MM-dd") と一致しているので、Date に変換できます。
年齢の入力欄を type="text" にしている理由
数値の入力欄には type="number" もありますが、この教材では type="text" にしています。第13章で、「数値の欄に文字を入力したときのエラー」をサーバー側でチェックする体験をするためです。
th:action と method="post"
th:action="@{/employees/register/confirm}" は、「確認」ボタンを押したときの送信先です。入力内容を送るので、method="post" にしています。この送信先は、10.5 で作った confirm メソッド(@PostMapping("/employees/register/confirm"))です。
確認画面のテンプレート
確認画面では、<table> に th:object="${registerForm}" を付けています。こうすると、その中では *{name} のように *{ } でフォームのフィールドを短く書けます。*{name} は ${registerForm.name} と同じ意味です。
| 書き方 | 表示される内容 |
|---|---|
*{name} |
フォームの氏名 |
th:switch="*{gender}" |
フォームの性別。1 なら「男」、2 なら「女」を表示する(第8章と同じ) |
*{#dates.format(birthDate, 'yyyy/MM/dd')} |
生年月日を「2001/06/15」の形にしたもの(第8章と同じ) |
${department?.deptName} |
Controller で Model に入れた部署の部署名 |
${department?.deptName} の「?.」
フォームに入っているのは部署IDだけです。部署名を表示するために、confirm メソッドで部署IDから部署を検索し、"department" という名前で Model に入れています。
もし部署を選ばずに「確認」を押すと、部署IDは null なので部署が見つからず、department も null になります。このとき ${department.deptName} と書くと、null の部署名を取り出そうとしてエラーになってしまいます。
?. は「左側が null なら、エラーにせず何も表示しない」という書き方です。入力チェックは第13章で作るので、それまではこの書き方でエラーを防いでおきます。
10.11 よくあるエラー
| 症状・メッセージ | 主な原因と対処 |
|---|---|
Neither BindingResult nor plain target object for bean name 'registerForm' available as request attribute |
テンプレートの th:object="${registerForm}" に対応するオブジェクトが Model にない。model.addAttribute("registerForm", …) の書き忘れや、名前の打ち間違い(registForm など)を確認する |
| 部署のプルダウンに「選択してください」しか出ない | 部署の一覧を Model に入れる2行(model.addAttribute("departments", departments) など)の書き忘れ、または Model に入れた名前(departments)とテンプレートの ${departments} が一致していない |
NotSerializableException(開発中は起きないことが多く、本番の複数サーバー構成などで起きる) |
RegisterForm に implements Serializable を書き忘れている |
| 「新規登録」ボタンを押すと 404 |
@GetMapping("/employees/register") の URL と、list.html の th:href="@{/employees/register}" が一致しているか確認する |
入力画面の表示で TemplateInputException
|
テンプレートの場所が templates/employee/register/input.html になっているか、input() の戻り値 "employee/register/input" と一致しているか確認する |
| 「確認」を押すと 404 になる |
@PostMapping("/employees/register/confirm") の URL と、入力画面の th:action が一致していない。URL を1文字ずつ見比べる |
| 確認画面の部署が空欄になる | Controller で Model に入れた名前("department")と、テンプレートの ${department?.deptName} の名前が違う。名前を1文字ずつ見比べる |
確認画面で 500 エラー(TemplateProcessingException) |
?. を付け忘れて ${department.deptName} と書き、部署を選ばずに確認した。${department?.deptName} と書く |
10.12 まとめと確認問題
まとめ
- HTTP は前のリクエストの内容を覚えていない(ステートレス)ため、画面をまたいでデータを覚えておくには セッション を使う
- セッションはサーバー側にあるブラウザごとの保管場所で、ブラウザは Cookie の
JSESSIONIDで自分の保管場所を示す - 画面の入力内容は、Entity とは別の フォームクラス で受け取る。セッションに保存するので
Serializableを実装する -
@SessionAttributes("registerForm")を付けると、Model のregisterFormが自動でセッションに保存・取り出しされる - Thymeleaf の
th:objectとth:fieldを使うと、フォームクラスと入力欄を簡単に結び付けられる -
@ModelAttributeを付けた引数には、セッションのフォームに送信された値を詰めたものが渡される
確認問題
問1 入力画面・確認画面・完了画面の間で入力内容を覚えておくために、セッションが必要なのはなぜですか。
解答を見る
HTTP はステートレスで、前のリクエストの内容を覚えていないためです。サーバー側の保管場所であるセッションに入力内容を保存しておくことで、次の画面でも使えるようになります。
問2 サーバーは、どのブラウザからのリクエストかを何で見分けていますか。
解答を見る
ブラウザの Cookie に保存された セッションID(JSESSIONID)です。ブラウザはリクエストのたびにセッションIDを送ります。
問3 @SessionAttributes("registerForm") を付けると、どうなりますか。
解答を見る
Model に registerForm という名前で入れたオブジェクトが自動でセッションに保存され、次のリクエストでは自動でセッションから取り出されて Model に入ります。
問4 RegisterForm に implements Serializable を書くのはなぜですか。
解答を見る
RegisterForm をセッションに保存するためです。本番の環境では、セッションの中身をデータベースなどに保存したり、ほかのサーバーへ送ったりすることがあります。そのため、保存できる形に変換できる(Serializable である)必要があります。
問5 入力画面のテンプレートで th:field="*{name}" と書くと、どのような属性が自動で作られますか。
解答を見る
id="name"、name="name"、そしてフォームに今入っている値を表す value="…" が自動で作られます。
問6 確認画面で ${department.deptName} ではなく ${department?.deptName} と書いているのはなぜですか。
解答を見る
部署が選ばれていないと department が null になり、${department.deptName} ではエラーになるからです。?. を使うと、左側が null のときはエラーにせず何も表示しません。




