JSF のビューテンプレート内で条件分岐してコンポーネントの表示を切り替えたい場合、 rendered
属性で表示するための条件を指定することができます。しかし rendered
属性は、 JSP の c:choose
などと違い排他構造を表現できないため、 JavaScript で使うためなどでコンポーネントに id
を指定している場合に重複エラーとなってしまいます。例えば以下のように書いた場合*1。
<h:form id="mainForm"> <h:outputText rendered="#{foo.showText}" id="hogehoge" value="#{foo.value1}" /> <h:outputText rendered="#{!foo.showText}" id="hogehoge" value="#{foo.value2}" /> </h:form>
rendered
に指定している条件は互いに排他なので実際に重複することはありませんが、文書構造上は排他でないので以下のようなエラーになります。
java.lang.IllegalStateException: Component ID mainForm:hogehoge has already been found in the view.
JSF でも JSTL のタグは使えるので、 c:choose
を使うという手もないではない*2ですがなんとも微妙だなーなどと Twitter でつぶやいていたところ、 @den2sn さんにパススルーアトリビュートなるものの存在を教えていただきました。
パススルーアトリビュート( Pass-through attributes )とは
任意の HTML の属性を指定してそのまま出力するための機能です。 JSF 2.2 から使えるようになったようです。使い方は JavaEE 7 HTML5に対応したJSF 2.2 - しんさんの出張所 はてな編 が詳しいです。
パススルーアトリビュートを使うには、 XML 名前空間を追加するだけです。プレフィックスには p
を使う例が多いですが、 PrimeFaces とかぶるので pt
にしています。
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
先ほどの例で id
で指定していたのを pt:id
に書き換えます。
<h:form id="mainForm"> <h:outputText rendered="#{foo.showText}" pt:id="hogehoge" value="#{foo.value1}" /> <h:outputText rendered="#{!foo.showText}" pt:id="hogehoge" value="#{foo.value2}" /> </h:form>
これで HTML 上では id="hogehoge"
として無事出力することができます。なお、普通に id
を指定した場合とは HTML 上の id が異なる*3ため、 JavaScript で参照している場合は修正が必要です。
そもそも JSF が生成する id を JavaScript から参照するのも非常に微妙な感あるので、そういう場合は基本パススルーアトリビュートを使うのが良いような気もします。