開発部の上條です。
早いものでもう3月。すっかり年度末です。
そこで私も、これまでに携わってきた開発業務の中で直面した課題や、その解決策を自分なりに振り返っていきたいと思います。
今回の課題:「画面遷移でき…てない!?」
私が入社して間もない頃。JSF(JavaServer Faces)によるwebアプリケーションの開発を行っていた私は、「フォームへの入力内容をボタン押下でデータベースに登録し、遷移先の別画面に表示させる」機能の実装に取り組んでいました。その結果として、事前に先輩エンジニアの方から教わっていたPrimeFacesというコンポーネントを利用し以下のように実装しました。
<h:form> | |
<p:outputLabel value="各項目を入力してください。" /><br/> | |
<p:outputLabel for="item1" value="項目1:" /> | |
<p:inputText required="true" id="item1" value="#{ register.item1}"/> | |
<br/> | |
<p:outputLabel for="item2" value="項目2:" /> | |
<p:inputText required="true" id="item2" value="#{ register.item2}"/> | |
<br/> | |
<p:outputLabel for="item2" value="項目3:" /> | |
<p:inputText required="true" id="item3" value="#{ register.item3}"/> | |
<br/> | |
<p:commandButton value="登録" type="submit" action="#{ register.execute()}" /> | |
</h:form> |
<p:outputLabel value="正常に登録しました。" /><br/> | |
<p:outputLabel for="item1" value="項目1:#{ register.item1}" /><br/> | |
<p:outputLabel for="item2" value="項目2:#{ register.item2}" /><br/> | |
<p:outputLabel for="item2" value="項目3:#{ register.item3}" /> |
private String item1; | |
private String item2; | |
private String item3; | |
public String execute() { | |
〜省略(データベースへの登録処理)〜 | |
return “/registered.xhtml”; | |
} | |
public String getItem1() { | |
return item1; | |
} | |
public void setItem1(String item1) { | |
this.item1 = item1; | |
} | |
public String getItem1() { | |
return item1; | |
} | |
public void setItem1(String item2) { | |
this.item2 = item2; | |
} | |
public String getItem3() { | |
return item3; | |
} | |
public void setItem3(String item3) { | |
this.item3 = item1; | |
} |
ところが!こちらのコード、実際に動かして登録ボタンを押してみると…
当時の私「…?」
当時の私「あれ…?画面変わらない……なんで??」
そうです、このコードでは画面遷移しません。これは、なぜでしょうか?
この課題の原因は?
この問題を解決するキーワードとなるのは、タイトルにもある「Ajax」です。AjaxとはJavaScriptとXMLを用いて非同期通信を可能にしたもので、非同期通信自体は「ブラウザ画面全体の再読み込みをしないで表示内容を更新させたい!」という時によく用いられます。反対に、ブラウザ画面全体の再読み込みによって画面更新を行うのは同期通信と呼ばれます。webページの遷移時に使われるのはこちらの通信方法です。
さて、今回のコードでボタンを実装するのに使っているcommandButtonタグですが、PrimeFacesのドキュメントを確認するとAjaxを利用するかどうかの設定がデフォルトでオン(ajax属性=true)になっています。つまり、このタグで実装したボタンを押下した時、特に設定を変更しなければAjaxによる非同期通信で処理が実行されます。ですが、画面遷移の処理は同期通信で行わなければなりません。
なるほど、そりゃあ上手くいかないわけですよね…(^^;)
同期通信ではサーバーに対してwebページ全ての情報をリクエストします。全ての情報をリクエストするので画面全体の再読み込みを行うことになります。ただし、サーバーからレスポンスが戻ってこないと、ブラウザ側では他の作業が一切できません。そのため、レスポンスを受け取るまでの待機時間が発生してしまい、少しの間ブラウザ上は真っ白な画面になります。
一方、非同期通信ではwebページの一部情報のみをサーバーにリクエストします。ページ全体のリクエストではないので、レスポンスが返ってくるまでの間にリクエストを出さなかった部分の描画処理が可能です。そのため、画面全体の再読み込みを行わずとも画面更新が可能となります。
解決策は?
そんなわけで今回の問題は、登録ボタンを押した際にAjaxによる非同期通信を行わないようにすれば無事解決です。そのためには、以下のように遷移前の画面(form.xhtml)で定義しているcommandButtonタグのajax属性を、デフォルトのtrue(有効)ではなく、false(無効)にすればOKです。
<h:form> | |
<p:outputLabel value="各項目を入力してください。" /><br/> | |
<p:outputLabel for="item1" value="項目1:" /> | |
<p:inputText required="true" id="item1" value="#{ register.item1}"/> | |
<br/> | |
<p:outputLabel for="item2" value="項目2:" /> | |
<p:inputText required="true" id="item2" value="#{ register.item2}"/> | |
<br/> | |
<p:outputLabel for="item2" value="項目3:" /> | |
<p:inputText required="true" id="item3" value="#{ register.item3}"/> | |
<br/> | |
<p:commandButton value="登録" type="submit" action="#{ register.execute()}" ajax=”false” /> | |
</h:form> |
この実装に修正して実行したところ…
今度は無事に画面遷移してくれました。
「良かった…よし、この調子でどんどん進めていくぞー!」
というモチベーションとは裏腹に、このアプリケーションの開発で再び壁にぶつかることになるのですが…
それは後日お話ししましょう。それでは、また次回!
<次回の記事>
何でこうなるの?現場で起きた開発回顧録【その2-JSFライフサイクルとimmediate】