今回もあまり記事を見かけなかったのでメモ。


ui:repeatでテーブルを作り、行ごとに更新ボタンを置いて、その行だけ更新をしたいような場合。
画面全体を送ってしまうと、JSFの標準バリデーションを使っている場合対象の行以外にもバリデーションが走ってしまう。できれば、対象の行だけバリデーションして、更新したい。

こういう場合はajax便利ですね。


色々と記事を見ていると、ui:repeat内から、例えばリスト全体とか、table全体とかを更新する方法については結構言及されているんですけど列単位での更新とかについてはほとんど記事無し。
実際やってみてわかったんですが、それほど難しいテクニックがいるわけではないので誰も言及していないだけかもしれません。
わたしは悩みましたが・・・というか、いろいろ試してわかったというべきでしょうかね。


今回は、サンプルとしてtableを使用した例を作成しました。
テキストボックスとボタンと表示用領域の3カラムが並んだ列があって、「更新」ボタンを押すと、テキストボックスに入力した内容が表示用領域に表示される。
バリデーションが期待通りに動いているのを確認するため、バリデーションチェックも追加しておきました。(文字数チェック)


■Bean
パラメータはこんな感じで。@PostConstructで配列を初期化しています。
これがないとui:repeat内の送信データが受け取れないので。
updateでは受け取ったデータが正しいかとりあえず出力して確認しています。
@Controller
@Scope("request")
public class TestBean {

private List<String> textList;

public List<String> getTextList() {
return textList;
}

public void setTextList(List<String> textList) {
this.textList = textList;
}

@PostConstruct
private void init() {
this.textList = new ArrayList<String>();
for (int i=0; i<10; i++) {
this.textList.add("");
}
}

public void update() {
System.out.println(this.getTextList().get(0));
System.out.println(this.getTextList().get(1));
System.out.println(this.getTextList().get(2));
System.out.println(this.getTextList().get(3));
System.out.println(this.getTextList().get(4));
System.out.println(this.getTextList().get(5));
System.out.println(this.getTextList().get(6));
System.out.println(this.getTextList().get(7));
System.out.println(this.getTextList().get(8));
}
}



■xhtml
細かいところは省略して、メインのui:repeat部分のみ
<h:form id="f" prependId="false">

<table>
<ui:repeat id="rep" value="#{testBean.textList}" varStatus="stat">
<tr jsf:id="row">
<td>
<h:inputText id="listText" value="#{testBean.textList[stat.index]}" label="テキスト">
<f:validateLength maximum="10" minimum="3" />
</h:inputText>
<h:message for="listText"/>
</td>
<td>
<f:ajax execute=":f:rep:row" render=":f:rep:row">
<h:commandButton styleClass="btn btn-default" action="#{testBean.update}" value="更新"/>
</f:ajax>
</td>
<td>
<h:outputText value="#{testBean.textList[stat.index]}" />
</td>
</tr>
</ui:repeat>
</table>

</h:form>


こんな形で実現できました。

ポイントは、
・execute、renderはidを使ったフルパス指定
・jsf:idを使って、更新したい範囲にidを設定しておく
というあたりでしょうか。

varStatusのindex等を使って色々としないといけないのかな・・・と思っていたんですが
ちゃんとexecute内のidにも配列処理をしてくれるんですね。
ソースコード等で見ると、button内のonClickで":f:rep:0:row"と対象行の
インデックス付きで指定されているのがわかります。