Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Thymeleafを使用した入力フォームのサンプルコード

More than 1 year has passed since last update.

概要

Spring bootとテンプレートエンジンのthymeleafを使用した入力フォームのサンプルコードです。

Thymeleaf 3.0を利用した記事「Thymeleaf 3.0を使用した入力フォームのサンプル 」を投稿しました。(2018/04/14)

環境

下記の環境で動作確認を行いました。

  • Windows7 (64bit)
  • Java 1.8.0_45
  • thymeleaf 2.1.4
    • Spring boot 1.2.4

参考

下記のサイトを参考にさせていただきました。

Thymeleaf

サンプルコード

Spring Bootで簡単な検索アプリケーションを開発するで作成した検索アプリケーションに、今回の入力フォームのサンプルコードを追加しました。プロジェクトの構成などはこちらの記事から確認できますので当記事では省略します。

完成図
index
FireShot Capture - SimpleForm index - http___localhost_9000_simple_.png
confirm
FireShot Capture - SimpleForm confirm - http___localhost_9000_simple_confirm.png

form

パッケージ: com.example.actor.web

入力フォームの値を格納するフォームクラスです。
(長くなるのでgetter/setterは省略します。)

SimpleForm.java
package com.example.actor.web;

import java.io.Serializable;
import java.time.LocalDate;

import javax.validation.constraints.Digits;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.validator.constraints.Email;
import org.springframework.format.annotation.DateTimeFormat;

public class SimpleForm implements Serializable {

  private static final long serialVersionUID = -157143280035400042L;

  @NotNull
  @Size(min = 1, max = 120)
  private String ftext;

  @Pattern(regexp = "((19|[2-9][0-9])[0-9]{2})/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])")
  private String ftdate;

  @DateTimeFormat(pattern = "yyyy/MM/dd")
  private LocalDate fdate;

  @Digits(integer = 3, fraction = 0)
  private String ftnum;

  @Min(1)
  @Max(999)
  private Integer fnum;

  @Size(min = 1, max = 600)
  private String farea;

  @Email
  private String femail;

  @NotNull
  @Size(min = 6, max = 12)
  private String fpass;

  @Pattern(regexp = "A|B|C|D|E")
  private String fselect;

  private String[] fmselect;

  @Pattern(regexp = "on")
  private String fcheck;

  private String[] fchecks;

  @NotNull
  @Pattern(regexp = "A|B|C|D|E")
  private String fradio;

  // getter/setterは省略します

  public String getFareaNl2br() {
    if (StringUtils.isNotEmpty(this.farea)) {
      return this.farea.replaceAll("\n", "<br/>");
    }
    return "";
  }

  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE);
  }

}

:exclamation: Stringの配列に対するバリデーションの設定方法が分からなかったので、バリデーションを行っていません。

textareaで入力した値に含まれる改行コードをwebページへ出力する際に<br/>タグへ変換する機能(play frameworkのnl2brのような)が無いようなので、formに変換メソッドを定義しました。
テンプレート側では、このフィールド値を下記のようにth:utextでエスケープせずに出力します。

confirm.html
<td th:utext="*{fareaNl2br}"></td>

controller

パッケージ: com.example.actor.web

入力フォームを表示するアクション(index)と、入力フォームの値を受け取るアクション(confirm)を定義します。

SimpleController.java
package com.example.actor.web;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(value = "/simple")
public class SimpleController {
  final static Logger logger = LoggerFactory.getLogger(SimpleController.class);

  /**
   * selectの表示に使用するアイテム
   */
  final static Map<String, String> SELECT_ITEMS =
    Collections.unmodifiableMap(new LinkedHashMap<String, String>() {
    {
      put("select_A", "A");
      put("select_B", "B");
      put("select_C", "C");
      put("select_D", "D");
      put("select_E", "E");
    }
  });

  /**
   * check boxの表示に使用するアイテム
   */
  final static Map<String, String> CHECK_ITEMS =
    Collections.unmodifiableMap(new LinkedHashMap<String, String>() {
    {
      put("checkbox_A", "A");
      put("checkbox_B", "B");
      put("checkbox_C", "C");
      put("checkbox_D", "D");
      put("checkbox_E", "E");
    }
  });

  /**
   * radio buttonの表示に使用するアイテム
   */
  final static Map<String, String> RADIO_ITEMS =
    Collections.unmodifiableMap(new LinkedHashMap<String, String>() {
    {
      put("radio_A", "A");
      put("radio_B", "B");
      put("radio_C", "C");
      put("radio_D", "D");
      put("radio_E", "E");
    }
  });

  @InitBinder
  public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
  }

  @RequestMapping(value = "/", method = RequestMethod.GET)
  public String index(SimpleForm form, Model model) {
    model.addAttribute("selectItems", SELECT_ITEMS);
    model.addAttribute("checkItems", CHECK_ITEMS);
    model.addAttribute("radioItems", RADIO_ITEMS);
    return "Simple/index";
  }

  @RequestMapping(value = "/confirm", method = RequestMethod.POST)
  public String confirm(@Validated @ModelAttribute SimpleForm form, BindingResult result, Model model) {
    if (result.hasErrors()) {
      model.addAttribute("validationError", "不正な値が入力されました。");
      return index(form, model);
    }
    return "Simple/confirm";
  }

}

template

index.html

src/main/resources/templates/Simple/index.html

入力フォームを表示するテンプレートです。

index.html
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head th:replace="_temp :: header ('SimpleForm index')">
</head>
<body>

  <div class="container">

    <div class="page-header">
     <h1>SimpleForm index</h1>
     <p th:if="${validationError}" th:text="${validationError}">validation error</p>
    </div>

    <div class="row">
      <div class="col-md-12">

        <form role="form" action="/simple/confirm" th:action="@{/simple/confirm}" th:object="${simpleForm}" method="post">
          <!-- text -->
          <div class="form-group">
            <label class="control-label" for="inputText"><abbr title="required">*</abbr> Text</label>
            <input type="text" class="form-control" id="inputText" name="ftext" th:field="*{ftext}" />
            <span th:if="${#fields.hasErrors('ftext')}" th:errors="*{ftext}" class="help-block">error!</span>
          </div>
          <!-- date type text -->
          <div class="form-group">
            <label class="control-label" for="inputDateText">Date type text</label>
            <input type="text" class="form-control" id="inputDateText" name="ftdate" th:field="*{ftdate}" />
            <span th:if="${#fields.hasErrors('ftdate')}" th:errors="*{ftdate}" class="help-block">error!</span>
          </div>
          <!-- date -->
          <div class="form-group">
            <label class="control-label" for="inputDate">Date</label>
            <input type="text" class="form-control" id="inputDate" name="fdate" th:field="*{fdate}" />
            <span th:if="${#fields.hasErrors('fdate')}" th:errors="*{fdate}" class="help-block">error!</span>
          </div>
          <!-- number type text -->
          <div class="form-group">
            <label class="control-label" for="inputNumberText">Number type text</label>
            <input type="text" class="form-control" id="inputNumberText" name="ftnum" th:field="*{ftnum}" />
            <span th:if="${#fields.hasErrors('ftnum')}" th:errors="*{ftnum}" class="help-block">error!</span>
          </div>
          <!-- number -->
          <div class="form-group">
            <label class="control-label" for="inputNumber">Number</label>
            <input type="text" class="form-control" id="inputNumber" name="fnum" th:field="*{fnum}" />
            <span th:if="${#fields.hasErrors('fnum')}" th:errors="*{fnum}" class="help-block">error!</span>
          </div>
          <!-- textarea -->
          <div class="form-group">
            <label class="control-label" for="inputTextarea1">Textarea</label>
            <textarea rows="3" cols="80" class="form-control" id="inputTextarea" name="farea" th:field="*{farea}"></textarea>
            <span th:if="${#fields.hasErrors('farea')}" th:errors="*{farea}" class="help-block">error!</span>
          </div>
          <!-- email -->
          <div class="form-group">
            <label class="control-label" for="inputEmail">Email</label>
            <input type="email" class="form-control" id="inputEmail" name="femail" th:field="*{femail}" />
            <span th:if="${#fields.hasErrors('femail')}" th:errors="*{femail}" class="help-block">error!</span>
          </div>
          <!-- password -->
          <div class="form-group">
            <label class="control-label" for="inputPassword"><abbr title="required">*</abbr> Password</label>
            <input type="password" class="form-control" id="inputPassword" name="fpass" th:field="*{fpass}" />
            <span th:if="${#fields.hasErrors('fpass')}" th:errors="*{fpass}" class="help-block">error!</span>
          </div>
          <!-- select single -->
          <div class="form-group">
            <label class="control-label" for="exampleSelect">Select</label>
            <select class="form-control" id="exampleSelect" name="fselect">
              <option value="">---</option>
              <option th:each="item : ${selectItems}" th:value="${item.value}" th:text="${item.key}" th:selected="${item.value} == *{fselect}">pulldown</option>
            </select>
            <span th:if="${#fields.hasErrors('fselect')}" th:errors="*{fselect}" class="help-block">error!</span>
          </div>
          <!-- select multi -->
          <div class="form-group">
            <label class="control-label" for="exampleMSelect">Multi Select</label>
            <select class="form-control" id="exampleMSelect" name="fmselect" multiple="multiple" size="6">
              <option value="">---</option>
              <option th:each="item : ${selectItems}" th:value="${item.value}" th:text="${item.key}" th:field="*{fmselect}">pulldown</option>
            </select>
            <span th:if="${#fields.hasErrors('fmselect')}" th:errors="*{fmselect}" class="help-block">error!</span>
          </div>
          <!-- checkbox single -->
          <div class="form-group">
            <label class="control-label">Single checkbox</label>
            <div class="checkbox">
              <label>
                <input type="checkbox" name="fcheck" value="on" th:field="*{fcheck}" /> Active
              </label>
            </div>
            <span th:if="${#fields.hasErrors('fcheck')}" th:errors="*{fcheck}" class="help-block">error!</span>
          </div>
          <!-- checkbox multi -->
          <div class="form-group">
            <label class="control-label">Multi checkbox</label>
            <div class="checkbox" th:each="item : ${checkItems}">
              <label>
                <input type="checkbox" name="fchecks" th:value="${item.value}" th:text="${item.key}" th:field="*{fchecks}">checkbox</input>
              </label>
            </div>
            <span th:if="${#fields.hasErrors('fchecks')}" th:errors="*{fchecks}" class="help-block">error!</span>
          </div>
          <!-- radio -->
          <div class="form-group">
            <label class="control-label"><abbr title="required">*</abbr> Radio</label>
            <div class="radio" th:each="item : ${radioItems}">
              <label>
                <input type="radio" name="fradio" th:value="${item.value}" th:text="${item.key}" th:field="*{fradio}">radio</input>
              </label>
            </div>
            <span th:if="${#fields.hasErrors('fradio')}" th:errors="*{fradio}" class="help-block">error!</span>
          </div>
          <button type="submit" class="btn btn-default">confirm</button>
        </form>

      </div>
    </div>

    <div th:replace="_temp :: footer"></div>
  </div>

  <div th:include="_temp :: script"></div>

</body>
</html>

confirm.html

src/main/resources/templates/Simple/confirm.html

入力された値を表示するテンプレートです。

confirm.html
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head th:replace="_temp :: header ('SimpleForm confirm')">
</head>
<body>

  <div class="container">

    <div class="page-header">
     <h1>SimpleForm confirm</h1>
    </div>

    <div class="row">

      <div class="col-md-12">

        <table class="table table-striped table-bordered" th:object="${simpleForm}">
          <tr>
            <th class="col-md-3">Text (ftext)</th>
            <td class="col-md-9" th:text="*{ftext}"></td>
          </tr>
          <tr>
            <th>Date type text (ftdate)</th>
            <td th:text="*{ftdate}"></td>
          </tr>
          <tr>
            <th>Date (fdate)</th>
            <td th:text="*{fdate}"></td>
          </tr>
          <tr>
            <th>Number type text (ftnum)</th>
            <td th:text="*{ftnum}"></td>
          </tr>
          <tr>
            <th>Number (fnum)</th>
            <td th:text="*{fnum}"></td>
          </tr>
          <tr>
            <th>Textarea (farea)</th>
            <td th:utext="*{fareaNl2br}"></td>
          </tr>
          <tr>
            <th>Email (femail)</th>
            <td th:text="*{femail}"></td>
          </tr>
          <tr>
            <th>password (fpass)</th>
            <td th:text="*{fpass}"></td>
          </tr>
          <tr>
            <th>single select (fselect)</th>
            <td th:text="*{fselect}"></td>
          </tr>
          <tr>
            <th>multi select (fselect)</th>
            <td>
              <p th:each="c : *{fmselect}" th:text="${c}"></p>
            </td>
          </tr>
          <tr>
            <th>single checkbox (fcheck)</th>
            <td th:text="*{fcheck}"></td>
          </tr>
          <tr>
            <th>multi checkbox (fchecks)</th>
            <td>
              <p th:each="c : *{fchecks}" th:text="${c}"></p>
            </td>
          </tr>
          <tr>
            <th>radio (fradio)</th>
            <td th:text="*{fradio}"></td>
          </tr>
        </table>

      </div>
    </div>

    <div th:replace="_temp :: footer"></div>
  </div>

  <div th:include="_temp :: script"></div>
</body>
</html>
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away