<apex:input>タグでvalueを配列の要素で指定するとtypeが指定できないエラーの回避方法


 Visualforceの作成中にユーザ入力の利便性向上のために、入力欄のtypeを指定したいという要件がありました。
(例.電話番号の入力欄は数字のキーパッドを表示する)

このとき
Apex側で
String stringVar {get; set;}
と宣言している変数を使用して、
<apex:input value="{!stringVar}" type="tel">
であれば、問題なくコンパイルが通ります。

しかし、
List<String> stringList {get; set;}
を使用して
<apex:input value="{!stringList[0]}" type="tel">
とすると、コンパイルエラーとなります。
(エラー文:Expected input type 'text', got 'tel' for Id data type)
(変数側がdate型で、type="date"の場合や、ListではなくMapの場合も同様のエラーとなります。)

今回の要件では、上記の入力欄を動的なListの数だけapex:repeatで表示しようとするため、どうしても配列を使う必要がありました。
(まあ、上記の動的なリストの最大値の数だけクラス定義したオブジェクトを用意して、<apex:input>を静的に書いてvalueを指定し、renderdで表示を制御するという方法を使えば配列を使わなくても出来たのですが、現実的ではありませんでした。)

上記のエラー文で検索したところ、フォーラムでいくつか同様の事象に関する質問が挙げられておりました。
回答としてはhtml標準のinputタグを使って、JavaScriptでApexに値を渡してあげるというものくらいしかありませんでした。
フォーラム1
フォーラム2

今回の回避策

<apex:input value="{!stringList[0]}" type="auto" id="hoge">
<script>
    document.getElementById('hoge').setAttribute("type", "tel");
<script>

typeをautoで指定してあげれば、コンパイルが通るので、初期表示時にJSでtype属性だけ書き換えてあげることで要件を満たすことが出来ました。
この際、他の箇所のrerenderで当該範囲を指定している場合は、注意が必要です。
rerenderされる範囲に<script>を記載しておかないと、初期表示時だけしかtypeが適用されません。

まとめ

<apex:input>タグではなく<input>タグを使う方法よりこの方法の方が、コントローラー側での処理の記載などが少ないため、良い方法だと個人的には思いました。