JSFでid属性を指定する方法

- 環境
    - CentOS Linux release 7.8.2003 (Core)
    - Eclipse IDE for Enterprise Java Developers.Version: 2020-03 (4.15.0)
    - openjdk version "11.0.7" 2020-04-14 LTS
    - JSF 2.3.9

JSFではid属性値がxhtmlでしてした値と異なる値で出力されることがある。
本にはname属性について記載されていたがid属性もコンポーネントツリーと対応関係がわかるようになっているのかもしれない。

> FacesServletは、コンポーネントツリーからHTMLファイルを生成してブラウザに送信します。このような変換をレンダリングといいます。
> レンダリングしてHTMLファイルにした後でも、内部にあるコンポーネントツリーとの対応関係がわかるように、バッキングビーンにバインドされるHTMLのタグには、次に示すようなname属性が埋め込まれます。
> 番号 : \<input type="text" name="j_idt6:j_idt8" />\<br />
> [わかりやすいJava EE ウェブシステム入門 - 秀和システム](https://www.shuwasystem.co.jp/book/9784798042169.html)



# 問題1 : h:formタグの内側でid属性は指定した値にならない

## h:formタグの外側では指定した通りのid属性値になる
`h:inputText`のid属性値に**outOfForm**を指定すると

```xml
<h:inputText id="outOfForm" value="formタグの外側では指定した通りのidになる" />
```

`input`のid属性値は**outOfForm**になる。

```html:出力
<input id="outOfForm" type="text" name="outOfForm" value="formタグの外側では指定した通りのidになる">
```

## h:formタグの内側では「formタグのid属性値:指定したid属性値」になる

### h:formタグにid属性を指定しないと「自動の属性値:指定したid属性値」になる
`h:form`タグのid属性は指定しないで、`h:inputText`のid属性値に**inOfForm**を指定すると

```xml
<h:form>
  <h:inputText id="inOfForm" value="h:formタグにid属性を指定しないと「自動の属性値:指定したid属性値」になる" />
</h:form>
```

`input`のid属性値は**j_idt5:inOfForm**になる。

```html:出力
<form id="j_idt5" name="j_idt5" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="j_idt5" value="j_idt5">
  <input id="j_idt5:inOfForm" type="text" name="j_idt5:inOfForm" value="h:formタグにid属性を指定しないと「自動の属性値:指定したid属性値」になる">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-6757148234097573315:8183558224686661226" autocomplete="off">
</form>
```

### h:formタグにid属性を指定すると「formタグのid属性値:指定したid属性値」になる
`h:form`タグのid属性値に**formId**、`h:inputText`のid属性値に**inOfForm**を指定すると

```xml
<h:form id="formId">
  <h:inputText id="inOfForm" value="h:formタグにid属性を指定すると「formタグのid属性値:指定したid属性値」になる" />
</h:form>
```

`input`のid属性値は**formId:inOfForm**になる。

```html:出力
<form id="formId" name="formId" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="formId" value="formId">
  <input id="formId:inOfForm" type="text" name="formId:inOfForm" value="h:formタグにid属性を指定すると「formタグのid属性値:指定したid属性値」になる">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="1982632179815695771:1244710352148896782" autocomplete="off">
</form>
```

## 解決策1 : h:formタグにprependId="false"を指定するとid属性値が指定した値になる

> **prependId**
> Flag indicating whether or not this form should prepend its id to its descendent's id during the clientId generation process. If this flag is not set, the default value is true.
> [JSF 2.2 View Declaration Language: Facelets Variant](https://docs.oracle.com/javaee/7/javaserver-faces-2-2/vdldocs-facelets/h/form.html)


(ざっくり訳)id生成中にformのIDをその子孫のIDの前に付加するかどうかを示すフラグ。デフォルト値はtrueです。
`h:form`タグのid属性値に**formId**、`h:inputText`のid属性値に**inOfForm**を指定すると

```xml
<h:form id="formId" prependId="false">
  <h:inputText id="inOfForm" value="h:formタグにprependId=falseを指定するとid属性が指定した値になる" />
</h:form>
```

`input`のid属性値は**inOfForm**になる。

```html:出力
<form id="formId" name="formId" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="formId" value="formId">
  <input id="inOfForm" type="text" name="inOfForm" value="h:formタグにprependId=falseを指定するとid属性が指定した値になる">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-3193809963809428842:-8198161006116065679" autocomplete="off">
</form>
```

# 問題2 : ui:repeatタグ内側でid属性値は指定した値にならない

## h:formタグの外側でも内側でも「自動の属性値:index番号:指定したid属性値」になる
`h:inputText`のid属性値に**inOfRepeat**を指定すると

```xml
<ui:repeat var="item" varStatus="stat" value="#{idBean.array}">
  <h:inputText id="inOfRepeat#{stat.index}" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない" />
</ui:repeat>
```

`input`のid属性値は**j_idt5:{index番号}:inOfRepeat**になる。

```html:出力
<input id="j_idt5:0:inOfRepeat" type="text" name="j_idt5:0:inOfRepeat">
<input id="j_idt5:1:inOfRepeat" type="text" name="j_idt5:1:inOfRepeat">
```

### h:formタグにid属性を指定しないと「自動の属性値:自動の属性値:index番号:指定したid属性値」になる
`h:form`タグのid属性は指定しないで、`h:inputText`のid属性値に**inOfRepeat{index番号}**を指定すると

```xml
<h:form>
  <ui:repeat var="item" varStatus="stat" value="#{idBean.array}">
    <h:inputText id="inOfRepeat#{stat.index}" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない" />
  </ui:repeat>
</h:form>
```

`input`のid属性値は**j_idt5:j_idt9:{index番号}:inOfRepeat**になる。

```html:出力
<form id="j_idt5" name="j_idt5" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="j_idt5" value="j_idt5">
  <input id="j_idt5:j_idt9:0:inOfRepeat" type="text" name="j_idt5:j_idt9:0:inOfRepeat">
  <input id="j_idt5:j_idt9:1:inOfRepeat" type="text" name="j_idt5:j_idt9:1:inOfRepeat">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="8291831575234858658:4144786523139149206" autocomplete="off">
</form>
```

### h:formタグにid属性を指定すると「formタグのid属性値:自動の属性値:index番号:指定したid属性値」になる
`h:form`タグのid属性値に**formId**、`h:inputText`のid属性値に**inOfRepeat{index番号}**を指定すると

```xml
<h:form id="formId">
  <ui:repeat var="item" varStatus="stat" value="#{idBean.array}">
    <h:inputText id="inOfRepeat#{stat.index}" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない" />
  </ui:repeat>
</h:form>
```

`input`のid属性値は**formId:j_idt9:{index番号}:inOfRepeat**になる。

```html:出力
<form id="formId" name="formId" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="formId" value="formId">
  <input id="formId:j_idt9:0:inOfRepeat" type="text" name="formId:j_idt9:0:inOfRepeat">
  <input id="formId:j_idt9:1:inOfRepeat" type="text" name="formId:j_idt9:1:inOfRepeat">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-9123594053057548780:-7376239985257011779" autocomplete="off">
</form>
```

### h:formタグにprependId="false"を指定してもid属性値は指定した値にならない
`h:form`タグに`prependId="false"`を指定しても

```xml
<h:form id="formId" prependId="false">
  <ui:repeat var="item" varStatus="stat" value="#{idBean.array}">
    <h:inputText id="inOfRepeat#{stat.index}" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない" />
  </ui:repeat>
</h:form>
```

`form`タグのid属性値がくっつかなくなるだけ。

```html:出力
<form id="formId" name="formId" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="formId" value="formId">
  <input id="j_idt9:0:inOfRepeat" type="text" name="j_idt9:0:inOfRepeat">
  <input id="j_idt9:1:inOfRepeat" type="text" name="j_idt9:1:inOfRepeat">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-915378070197893309:-5208595187307997788" autocomplete="off">
</form>
```

## 解決策1 : jsfのタグをやめてhtmlのタグで書く
注意 : id属性値が一意になるように考えて書かないと残念なことになる
とてつもなく微妙な策である。
`input`のid属性値に**inOfRepeat{index番号}**を指定すると

```xml
<h:form id="formId">
  <ui:repeat var="item" varStatus="stat" value="#{idBean.array}">
    <input tyep="text" id="inOfRepeat#{stat.index}" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない" />
  </ui:repeat>
</h:form>
```

`input`のid属性値は**inOfRepeat{index番号}**になる。
JSFワールドから外れているため`prependId="false"`がなくても`h:form`タグのid属性値が影響しない。

```html:出力
<form id="formId" name="formId" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="formId" value="formId">
  <input tyep="text" id="inOfRepeat0" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない">
  <input tyep="text" id="inOfRepeat1" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-3641841499499477715:2017944983286472561" autocomplete="off">
</form>
```

## 解決策2 : ui:repeatタグをやめてc:forEachタグを使う
注意 : id属性値が一意になるように考えて書かないと残念なことになる
JSTL Coreタグ(c:)は、JSPより継承されたタグなのでJSF的なところが薄いのか・・・

`h:form`タグのid属性値に**formId**、`h:inputText`のid属性値に**inOfRepeat{index番号}**を指定すると

```xml
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:f="http://xmlns.jcp.org/jsf/core"
  xmlns:c="http://java.sun.com/jsp/jstl/core"> <!--<<<追記-->
<h:form id="formId">
  <c:forEach var="item" varStatus="stat" items="#{idBean.array}">
   <h:inputText id="inOfRepeat#{stat.index}" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない"/>
  </c:forEach>
</h:form>
```

`input`のid属性値は**formId:inOfRepeat{index番号}**になる。

```html:出力
<form id="formId" name="formId" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="formId" value="formId">
  <input id="formId:inOfRepeat0" type="text" name="formId:inOfRepeat0" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない">
  <input id="formId:inOfRepeat1" type="text" name="formId:inOfRepeat1" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="7276136016284590450:-4018393601695065919" autocomplete="off">
</form>
```

`h:form`タグのid属性値をくっつけたくなければ`prependId="false"`を指定する。

```xml
<h:form id="formId" prependId="false">
...以降前と同じ...
```

```html:出力
<form id="formId" name="formId" method="post" action="/tryJsf/base.jsf" enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="formId" value="formId">
  <input id="inOfRepeat0" type="text" name="inOfRepeat0" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない">
  <input id="inOfRepeat1" type="text" name="inOfRepeat1" value="h:formタグの内側でui:repeatタグを使うとid属性値は指定した値にならない">
  <input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="-4294574413761401134:-7748270303136609237" autocomplete="off">
</form>
```