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

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

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

# 2021-11-15 追記

https://stackoverflow.com/questions/19497481/jsf-2-fparam-inside-of-uirepeat

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

#環境
##AP サーバー
GlassFish 3.1.2.2

#実装
```java: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 + "]";
    }
}
```

```java: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;
    }
}
```

```xml: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` 属性で値を指定するときに、インデックス値で指定すれば、入力もうまくマッピングできるようになる。

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

```xml:ダメパターン
<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>
```

#動作確認
##初期表示
![jsf_list_input_01.png](https://qiita-image-store.s3.amazonaws.com/0/28302/1bf8f4b5-c359-8092-683f-45f4da7ad78f.png)

##値を編集してサブミット
![jsf_list_input_02.png](https://qiita-image-store.s3.amazonaws.com/0/28302/e1cf5c41-09b8-5be4-ec80-eccbf8d48ec0.png)

##サブミット時の標準出力
```text:
情報: [KeyValue [key=aaa, value=AAA], KeyValue [key=bbb, value=BBB], KeyValue [key=ccc, value=CCC]]
```

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

#参考
- [jsf - How map multiple inputText to an array property? - Stack Overflow](http://stackoverflow.com/questions/6741573/how-map-multiple-inputtext-to-an-array-property)