JSFでループで生成した入力項目をバッキングBeanのプロパティとマッピングする方法


バッキングBean が List のプロパティを持っていたとする。

これを、 xhtml 上でループを回しながら出力するのは簡単だけど、入力項目としてマッピングするにはどうすればいいのか、簡単にはわからなかったのでメモ。

2021-11-15 追記

  • 上記 Stackoverflow によると、 ui:repeat は view tree が構築されたあとに処理されるのに対して、パラメータの処理は view tree の構築中に処理されるという違いがあり、それが原因でバインドができないらしい
  • <c:forEach> でループさせれば、かなり簡潔に書けるもよう
  • ただし、試してみたところバッキングビーンは @ViewScoped である必要があった(@RequestScoped だと、パラメータが渡せなかった)

環境

AP サーバー

GlassFish 3.1.2.2

実装

KeyValue.java
package sample.jsf;

public class KeyValue {
    private String key;
    private String value;

    public KeyValue(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "KeyValue [key=" + key + ", value=" + value + "]";
    }
}
HelloBean.java
package sample.jsf;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class HelloBean {

    private List<KeyValue> list;

    public void method() {
        System.out.println(this.list);
    }

    @PostConstruct
    public void init() {
        this.list = new ArrayList<>();
        this.list.add(new KeyValue("hoge", "HOGE"));
        this.list.add(new KeyValue("fuga", "FUGA"));
        this.list.add(new KeyValue("piyo", "PIYO"));
    }

    public void setList(List<KeyValue> list) {
        this.list = list;
    }

    public List<KeyValue> getList() {
        return list;
    }
}
hello.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
          PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html">
  <head>
    <title>index</title>
  </head>
  <body>
    <form jsfc="h:form">
      <div jsfc="ui:repeat" value="#{helloBean.list}" varStatus="loop">
        key:<input jsfc="h:inputText" value="#{helloBean.list[loop.index].key}" />
        value:<input jsfc="h:inputText" value="#{helloBean.list[loop.index].value}" />
      </div>
      <input jsfc="h:commandButton" action="#{helloBean.method}" value="submit" type="submit" />
    </form>
  </body>
</html>

入力項目(h:inputText)の value 属性で値を指定するときに、インデックス値で指定すれば、入力もうまくマッピングできるようになる。

以下のようにしてしまうと、入力のマッピングは動作しなくなる。

ダメパターン
<div jsfc="ui:repeat" value="#{helloBean.list}" var="item">
  key:<input jsfc="h:inputText" value="#{item.key}" />
  value:<input jsfc="h:inputText" value="#{item.value}" />
</div>

動作確認

初期表示

値を編集してサブミット

サブミット時の標準出力

情報: [KeyValue [key=aaa, value=AAA], KeyValue [key=bbb, value=BBB], KeyValue [key=ccc, value=CCC]]

ちゃんと入力した値がわたっている。

参考