JSF 2.0AjaxサポートとjQueryの連携
前回はjQueryはおまけ程度の扱いだったが、今回はjQuery全開でいくぞい。
JSF 2.0のAjaxは非常に強力ではあるが、
ぜひそれらと連携をしたい。
もちろん、JSF 2.0にはそれは用意されている。一応先にいっておくとjQuery前提ではないので、他のjavascriptのライブラリでもよい。
以前作成した足し算と引き算のプログラム。Ajax未対応なものと対応したものではコードは同じであったが、
ある。Ajax未対応なテンプレートとコードをそのままでAjax対応にすることが可能なのだ!
たとえば以下のテンプレートは以前足し算と引き算をやるものだった。
<body> <form jsfc="h:form" id="f"> <input jsfc="h:inputText" id="p1" value="#{calc.param1}"/> <input jsfc="h:commandButton" id="add" value="+" action="#{calc.add}" /> <input jsfc="h:commandButton" id="sub" value="-" action="#{calc.sub}" /> <input jsfc="h:inputText" id="p2" value="#{calc.param2}"/> = <input jsfc="h:outputText" id="r" value="#{calc.result}"/> </form> </body>
コードは以下の通り。
import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; @ManagedBean @ViewScoped public class Calc implements Serializable{ int param1; int param2; int result; public int getParam1() { return param1; } public void setParam1(int param1) { this.param1 = param1; } public int getParam2() { return param2; } public void setParam2(int param2) { this.param2 = param2; } public int getResult() { return result; } public String add(){ result = param1 + param2; return null; } public String sub(){ result = param1 - param2; return null; } }
この時点ではAjax未対応だ。
これをAjax対応にする。以下の1行をどこでもいいのでいれる。これはscriptの生成をするコード。targetの場所に自動で生成されるためにコンポーネントと一緒にまとめて管理できる。リソースの扱いはかなり楽になった。
<h:outputScript name="jsf.js" library="javax.faces" target="head"/>
続いてscript。ここではjQueryを利用してイベントをバインドした。もちろんhtmlにかいてもいいし、別ファイルにしてもよい。
$(document).ready(function(){ $("#f\\:add").click(function(event){ jsf.ajax.request(event.target , event, {execute:"f:p1 f:p2" , render:"f:r"} ); event.preventDefault(); }); $("#f\\:sub").click(function(event){ jsf.ajax.request(event.target , event, {execute:"f:p1 f:p2" , render:"f:r"} ); event.preventDefault(); }); });
「jsf.ajax.request」がポイント。これを自動生成してくれるのが「
「event.preventDefault();」は本来のイベントを実行しないという意味がある。submitボタンなのでこれがないとAjaxによるリクエストと画面遷移する通中のSubmitと2つ動いてしまう。
あと、jQueryで注意をしないといけないのがidにコロン等がある場合エスケープしないといけないこと。
項目が増えるとexecuteとrenderを指定するのが大変になると思うかもしれない。大概、フォームまとめて送信することが多いと思われるため以下のようにして丸ごと送ってもよい。
jsf.ajax.request(event.target , event, {execute:"f" , render:"f"} );
「f」というのが対象のフォームIDを示している。「f:@form」や「@form」でも動いているように見える。
ただし、ここで問題が起きる。
これはフォームを丸ごと再レンダリングしている。ということはボタンも再レンダリングされる。この時点でフォーム内に設定したイベントが吹っ飛ぶ。
つまり、1回はAjaxで実行されるが、次は画面遷移を伴うsubmitになる。Ajaxが2回に1回だけ成功しているので気持ち悪いことに。
この現象、jQueryとかガンガン使ったことがある人なら覚えがあるだろう。
ではどうするか。まず単純に考えられるのが毎回付け直すこと。executeなどのオプションに「onevent:function(){〜}」を指定するとよい。そうすると個別のイベントで前回やったイベントが返ってくる。つまり、1回のアクセスで3回、Ajax発行前、発行後、再レンダリング後と発行される。statusを見て分岐するとよいのも同じ。
jsf.ajax.request(event.target , event, { execute:"f" , render:"f" , onevent:function(event){ alert(event.status); //ここでイベントをつける。しかもフォーム全部の。 } } );
でもそれはあまりにもめんどくさい。そこでjQuery1.3の新機能のliveを使うとよい。これは後から作られてもイベントが発行可能だ。
$("#f\\:add").live("click",function(event){ jsf.ajax.request(event.target , event, { execute:"f" , render:"f" } ); event.preventDefault(); });
以上のようにAjax未対応なアプリをAjax対応させることをsciptだけで行うことが可能になっている。scriptから実行できるのでライブラリのモーダルな「はい/いいえ」ダイアログなどを用意して、選択されたらこっちを実行、再レンダリングする、などといったことが簡単に可能になる。
とはいえ、何がレンダリングされるか、イベントはいつ紐づいたか、消えてないかなどをしっかり把握する必要があり、基本は