Struts2と表形式入力
Webアプリケーションでシステム作るときに結構悩ましい画面要望の1つ、表形式の入力 と呼ばれるものがあります。
これ、結構簡単にみえて そもそも実装するのが面倒だった のですが、実はStruts2公開当時から実装ができました。
しかしそこからセキュリティフィックスや潜在バグなどさまざまな要因から二転三転し、バージョンによっては動かなくなる実装もありましたので、こちらで紹介します。
表形式の入力(リクエスト)方法
Struts2は、リクエストパラメータをActionクラスのメンバ変数に格納します。メンバ変数へ格納する際には、自動的にActionクラスのメンバ変数の型(クラス)に変換しようと試みます。自動的に変換する型(クラス)には以下のものがあります。
- プリミティブ型すべて
- プリミティブ型のラッパークラス
- java.util.Collection / List / Map
- 配列
- 何らかの実装クラス(POJOは必須ではない)
…つまり大抵のものは自動的に変換してくれます。
また、クラスのフィールドを自動的に探索して値を格納しますので、例えばActionクラスで次のフィールドを用意します。
private List<SampleProduct> products;
public void setProducts(List<SampleProduct> products) {
this.products = products ;
}
public List<String> getProducts () {
return products;
}
こうすることで、画面へのレスポンス時も、画面からのリクエストにも配列で扱えますし、かつその中身が独自に用意したクラスでも扱えます。
Thymeleafを使った入力フォームの例
<table class="table table-bordered">
<thead>
<th>id</th>
<th>製品名</th>
</thead>
<tbody>
<tr th:each="product,status : ${products}">
<td>
<input type="text" value="X-45" name="id" class="form-control" th:name="'products[' + ${status.getIndex()} + '].id'" th:value="${product.getId()}" />
</td>
<td>
<input type="text" value="X-45" name="name" class="form-control" th:name="'products[' + ${status.getIndex()} + '].name'" th:value="${product.getName()}" />
</td>
</tr>
</tbody>
</table>
<tr th:each>
を使うことでListなどの繰り返し出力ができます。
JSTLなどと異なるのは、繰り返しの件数や現在の行番号を管理するオブジェクトは、カンマ区切りの右側で宣言した名前で参照できることです。カンタンですね。
上記の例で、実際に出力されるHTMLは、次のようになります。
<table class="table table-bordered">
<thead>
<th>id</th>
<th>製品名</th>
</thead>
<tbody>
<tr>
<td>
<input type="text" class="form-control" name="products[0].id" value="0番目のid値" />
</td>
<td>
<input type="text" class="form-control" name="products[0].name" value="0番目のname値" />
</td>
</tr>
<tr>
<td>
<input type="text" class="form-control" name="products[1].id" value="1番目のid値" />
</td>
<td>
<input type="text" class="form-control" name="products[1].name" value="1番目のname値" />
</td>
</tr>
........以降、繰り返し.......
</tbody>
</table>
JSPの入力フォーム例
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<body>
<s:form action="update" theme="simple">
<s:iterator value="products" var="product" status="status">
<s:textfield name="products[%{#status.index}].id"></s:textfield>
<s:textfield name="prodocts[%{#status.index}].name"></s:textfield>
</s:iterator>
<s:submit />
</s:form>
</body>
</html>