AJAX 要求による Web ページの部分的な更新

Asynchronous JavaScript + XML (AJAX) という開発手法を利用すれば、ユーザーが編集する部分だけに限って、サーバーとの間で情報を中継する Web ページを作成でき、ページの残りの部分は表示させたままにしておくことができます。この手法を利用することで、ユーザーの操作の度にページ全体を再ロードする場合に比べて、Web アプリケーションを高速かつ効率的に実行できるようになります。EGL 制御の JSP ファイルをセットアップして、JSF ハンドラーの onPreRenderFunction を呼び出し、特定の更新内容のみをページに提供します。

AJAX 要求を使用できるように JSP ファイルと JSF ハンドラーをセットアップするには、以下の手順が一般的です。
  1. ページおよび JSF ハンドラーを作成します。
  2. ページを設計し、JSF ハンドラー内の変数および関数にバインドされている JSF コントロールを、ページに組み込みます。
  3. ページ内において、AJAX 要求の結果として変更させるエリアを指定します。ページの他の部分は、一切影響を受けません。
  4. AJAX 要求を起動するユーザー・イベントを、ページ上で指定します (特定のテキスト・コントロールの選択や、特定のテキスト・コントロールからの離脱など)。
  5. ページから JSF ハンドラーに送信する要求を定義します (要求のタイプ (更新、サブミット、または外部)、要求に組み込むパラメーターなど)。
  6. 要求を処理するコードを、JSF ハンドラー内に記述します。
EGL の標準的な AJAX ページのライフ・サイクルは、以下のとおりです。
  1. onConstructionFunctiononPreRenderFunctiononPostRenderFunction の各 JSF ハンドラー・プロパティーによって示されている関数を、サーブレットが必要に応じて実行して、ページ全体をレンダリングします。
  2. サーブレットによって、ページがブラウザーに送信されます。
  3. ユーザーが、ページ上の入力フィールドに入力を行います。
  4. ユーザーが AJAX 要求を起動します。
  5. 要求と、その要求で指定されているパラメーターが、ブラウザーによってサーブレットに送信されます。
  6. JSF ハンドラー内で onPreRenderFunction プロパティーによって示されている関数を、サーブレットが呼び出して、要求内のパラメーターをその関数に提供します。
  7. onPreRenderFunction 関数が実行されて、AJAX 要求によって指定されているページ上のエリア内にあるコントロールが更新されます。
  8. AJAX 要求によって指定されているページの一部が、サーブレットによってレンダリングされ、ブラウザーに表示されているページの該当箇所が更新されます。
  9. ユーザーがリンクをクリックするか、JSF ハンドラー内の forward ステートメントを起動するかして、他のページに移動するまで、AJAX 要求のサイクルは続行されます。
EGL の Web ページでは、以下の 3 つのタイプの AJAX 要求を使用することができます。
更新
このタイプの要求は、ページの指定エリア内にあるコントロールの値を更新するよう、サーブレットに促します。ただし、ページの状態に関する情報を JSF ハンドラーに提供することはしません。つまり、ユーザーがページ上の入力コントロールの値を変更しても、JSF ハンドラーはその変更を認識しません。JSF ハンドラー内の変数は、その変数がバインドされているページ・コントロールと一致するように更新されるわけではありません。このような制限があることで、要求時に送信されるデータ量が減少するため、要求のモジュール化と効率化を図ることができます。

更新要求を完了させるために、JSF ハンドラーがページからの情報を必要とする場合は、その要求にパラメーター (複数可) を追加することができます。JSF ハンドラーは、それらのパラメーターを要求とともに受け取ります。 ただし、前述のとおり、JSF ハンドラー内の変数は、その変数がバインドされているコントロールの新しい値には更新されません。詳しくは、 更新要求によるページの更新を参照してください。

サブミット
このタイプの要求は、JSF ハンドラー内の変数の値を更新するとともに、ページの指定エリア内にあるコントロールの値を更新するよう、サーブレットに促します。更新要求での処理とは異なり、サブミット要求では、JSF ハンドラー内のすべての変数に、それらの変数がバインドされているコンポーネントの現行値が設定されます。つまり、「ページの最新の状態に一致するようすべての変数が更新されるため、サブミット要 求と一緒にパラメーターを渡す必要はありません。詳しくは、サブミット要求によるページの更新を参照してください。
外部
このタイプの要求は、別のページのコンテンツを使用して、ページの指定エリア内にあるコンテンツを更新するよう、サーブレットに促します。詳しくは、 別のページの一部を利用したページの更新を参照してください。

J2EELib.getQueryParameter() システム関数は、パラメーターを AJAX 要求から取得します (パラメーターが存在する場合)。また、この関数を使用すると、$$ajaxmode パラメーターの値を検査することにより、AJAX 更新要求の結果または実行依頼要求の結果のどちらとして onPreRenderFunction 関数が呼び出されたかかを判別することもできます。 NULL 以外の値であれば、この関数は AJAX 更新要求またはサブミット要求の結果として呼び出されたということになります。

if (J2EELib.getQueryParmeter("$$ajaxmode") == null)
  //AJAX 更新要求とサブミット要求、いずれの結果でもありません。
  //AJAX 外部要求の結果だと思われます。
else
  //AJAX 要求の結果です。
end

この例では、AJAX 更新要求を使用して、電卓のような簡単な数学演算を実行します。入力コントロールが 2 つと、数学演算を含むコンボ・ボックスが 1 つ、ページ上に表示されます。AJAX 要求は、コンボ・ボックスによって起動され、選択された演算と 2 つの入力コントロールを JSF ハンドラーの onPreRenderFunction に渡します。 これによって、その数学演算が実行され、出力コントロールが更新されて解答が表示されます。

このページは、次の例のようになります。

ページと AJAX 動作の図

JSP ファイルのコードは、次のとおりです。

<html>
<head>
<title>calculatorPage</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="theme/stylesheet.css"
  title="Style">
</head>
<f:view>
  <body>
  <hx:scriptCollector id="scriptCollector1"
    preRender="#{calculatorPage._preRender}"
    postRender="#{calculatorPage._postRender}">

<h:form id="form1" styleClass="form">
<TABLE>
<TBODY>
  <tr>
  <td align="left">Field1:</td>
  <td style="width:5px"></td>
  <td>
    <h:inputText id="input1" value="#{calculatorPage.field1}"
      binding="#{calculatorPage.field1_Ref}" styleClass="inputText">
    </h:inputText>
  </td>
  </tr>
  <tr>
  <td align="left">Field2:</td>
  <td style="width:5px"></td>
  <td>
    <h:inputText id="input2" value="#{calculatorPage.field2}"
      binding="#{calculatorPage.field2_Ref}" styleClass="inputText">
    </h:inputText>
  </td>
  </tr>
  <tr>
  <td align="left">Operation:</td>
  <td style="width:5px"></td>
  <td>
    <h:selectOneMenu id="operationComboBox"
      styleClass="selectOneMenu" value="#{calculatorPage.operation}">
      <f:selectItem itemValue="add" itemLabel="add" />
      <f:selectItem itemValue="subtract" itemLabel="subtract" />
      <f:selectItem itemValue="multiply" itemLabel="multiply" />
      <f:selectItem itemValue="divide" itemLabel="divide" />
    </h:selectOneMenu>
    <hx:behavior event="onblur"
      target="operationComboBox" behaviorAction="get" 
      targetAction="updatablePanel"></hx:behavior></td>
  </tr>
  <tr>
  <td align="left">Output:</td>
  <td style="width:5px"></td>
  <td>
    <h:panelGroup id="updatablePanel" styleClass="panelGroup">
      <h:outputText id="output" value="#{calculatorPage.output}"
        binding="#{calculatorPage.output_Ref}" styleClass="outputText">
      </h:outputText>
    </h:panelGroup>
    <hx:ajaxRefreshRequest id="ajaxRefreshRequest1"
      target="updatablePanel" params="input1;input2;operationComboBox">
    </hx:ajaxRefreshRequest>
  </td>
  </tr>

</TBODY>
</TABLE>
</h:form>
  </hx:scriptCollector>
  </body>
</f:view>
</html>

このページと連携する JSF ハンドラーのコードは、次のとおりです。

package jsfhandlers;

handler calculatorPage type JSFHandler
  {onPreRenderFunction = onPreRender, 
    view = "calculatorPage.jsp"} 
    
    field1 float;
    field2 float;
    operation string;
    output string;

  function onPreRender()
    
    if (J2EELib.getQueryParameter("$$ajaxmode") == null)
      output = "Enter values and an operation.";
    else
      calculateAnswer();
    end
    
  end
  
  function calculateAnswer()
    param1 float = J2EELib.getQueryParameter("input1");
    param2 float = J2EELib.getQueryParameter("input2");
    
    case (J2EELib.getQueryParameter("operationComboBox"))
      when ("add")
        output = param1 + param2;
      when ("subtract")
        output = param1 - param2;
      when ("multiply")
        output = param1 * param2;
      when ("divide")
        output = param1 / param2;
    end
  end
end

フィードバック