ヘッダーをスキップ
Oracle® Fusion Middleware Oracle ADF FacesによるWebユーザー・インタフェースの開発
12c (12.1.2)
E48096-02
  目次へ移動
目次

前
 
次
 

5 ADF FacesでのJSFライフサイクルの使用

この章では、JSFページ・リクエストのライフサイクルとADF Facesからライフサイクルへの追加、およびアプリケーションでのライフサイクル・プロパティの使用方法について説明します。

この章では、次の項目について説明します。

5.1 JSFライフサイクルとADF Facesの使用について

ADF Facesアプリケーションでは、JSFライフサイクルとADF Facesライフサイクルの両方が使用されます。ただし、ADF FacesライフサイクルではJSFライフサイクルが拡張され、クライアント側の値のライフサイクル、1つのページに複数のフォームを使用する弊害(ユーザーの編集内容の消失など)なしに独立して送信可能なセクションをページに作成できるサブフォーム・コンポーネント、追加スコープなどの追加機能を提供します。

フレームワークによって提供されるライフサイクルの強化についてよりよく理解するには、標準のJSFライフサイクルについて理解することが重要です。この項の説明は概要にすぎません。詳細な説明は、http://www.jcp.org/en/jsr/detail?id=314にあるJSFの仕様を参照してください。

JSFページが送信され、新しいページがリクエストされると、JSFページ・リクエスト・ライフサイクルが起動されます。このライフサイクルでは、ページの値の送信、現在のページのコンポーネントの検証、コンポーネントのナビゲーションと結果ページへの表示および状態の保存とリストアが処理されます。FacesServletオブジェクトで、JSFアプリケーションのページ・リクエスト・ライフサイクルが管理されます。FacesServletオブジェクトで、リクエスト処理に必要な情報を含み、ライフサイクルを実行するオブジェクトを起動するFacesContextと呼ばれるオブジェクトが作成されます。

JSFライフサイクル・フェーズでは、UIコンポーネント・ツリーを使用してfacesコンポーネントの表示を管理します。このツリーはJSFページのランタイム表現で、ページ内の各UIコンポーネント・タグがツリーのUIコンポーネント・インスタンスに対応します。

図5-1に、ページ・リクエストのJSFライフサイクルを示します。ここに示すとおり、各フェーズの前後でイベントが処理されます。

図5-1 ADF Facesアプリケーションでのページ・リクエストのライフサイクル

ADFおよびJSFのフェーズの連携

JSFアプリケーションでは、ページ・リクエスト・ライフサイクルは次のようになります。

ライフサイクルの説明の一環として、ユーザーが日付を入力し、ボタンをクリックして入力値を送信する単純な入力テキスト・コンポーネントを持つページを考えてみます。valueChangeListenerメソッドもコンポーネントに登録されています。例5-1に、この例のコードを示します。

例5-1 JSFライフサイクルを説明するためのサンプル・コード

<af:form>
  <af:inputText value="#{mybean.date}"
      valueChangeListener="#{mybean.valueChangeListener}">
    <af:convertDateTime dateStyle="long"/>
  </af:inputText>
  <af:button text="Save"  actionListener="#{mybean.actionListener}"/>
</af:form>

ユーザーが文字列「June 25, 2015」を入力し、送信ボタンをクリックするとします。図5-2に、値がライフサイクルでどのように受け渡され、各イベントがどの時点で処理されるかを示します。

図5-2 JSFライフサイクルでの値とイベントの例

ライフサイクルでのページの例

5.2 immediate属性の使用

immediate属性を使用して、ライフサイクルのリクエスト値の適用フェーズまでコンポーネントの処理を進めることができます。actionSourceコンポーネント(buttonなど)がimmediateに設定されている場合、イベントはアプリケーションの起動フェーズではなくリクエスト値の適用フェーズで配信されます。actionListenerハンドラは、レスポンスのレンダリング・フェーズをコールします。

たとえば、「取消」ボタンをimmediateに構成し、アクションが前のページに戻るために使用される文字列を返すようにする場合があります(ナビゲーションの詳細は、第20章「ナビゲーション・コンポーネントの使用」を参照してください)。「取消」ボタンはimmediateに設定されているため、ユーザーが「取消」ボタンをクリックすると、図5-3に示すように、すべての検証がスキップされ、入力されたデータはモデルに更新されず、ユーザーは期待どおりにナビゲートします。

図5-3 immediateに設定されたボタンのライフサイクル

検証とモデルの更新を省略するライフサイクル

注意:

ナビゲーションを提供せず、immediateに設定されているボタンは、レスポンスのレンダリング・フェーズに直接進み、検証、モデルの更新、アプリケーションの起動のフェーズは省略され、新しい値はサーバーにプッシュされません。


表示イベントを呼び出すコンポーネント(showDetailコンポーネントなど)やeditableValueHolderコンポーネント(inputTextコンポーネントなど、変更可能な値を保持するコンポーネント)もimmediateに設定できます。actionSourceコンポーネントと同様に、イベントはリクエスト値の適用フェーズに配信されます。一方、editableValueHolderコンポーネントでは、フェーズをスキップするかわりに、valueChangeEventsイベントの変換、検証および配信が、検証の処理フェーズではなく、ライフサイクル初期のリクエスト値の適用フェーズで実行されます。スキップされるライフサイクル・フェーズはありません。

図5-4に、immediate属性がtrueに設定されている入力コンポーネントのライフサイクルを示します。入力コンポーネントは文字列として入力された日付を取得し、ボタンがクリックされたときにDateオブジェクトとしてその日付を格納します。

図5-4 入力コンポーネントのimmediate属性

入力コンポーネントのimmediate属性

入力コンポーネントのimmediate属性をtrueを設定すると、1つ以上の入力コンポーネントをその他のコンポーネントより前に検証する必要がある場合に役立ちます。これらのコンポーネントの1つで無効なデータが検出されると、同じページの他の入力コンポーネントの検証が省略されるため、ページに表示されるエラー・メッセージの数が削減されます。


パフォーマンスのヒント:

immediate属性をtrueに設定すると、パフォーマンスが向上する場合があります。

  • ナビゲーション・トレインを作成し、navigationPaneコンポーネント内にcommandNavigationItemコンポーネントがある場合、immediate属性をtrueに設定して、次のページへの移動時に、現在のページ(トレイン・ストップ)からのデータの処理を回避します。詳細は、20.9.1項「トレイン・モデルの作成方法」を参照してください。

  • 他の値の前に入力コンポーネント値を検証する必要がある場合、immediate属性をtrueに設定します。ライフサイクルの早い段階でエラーが検出され、その他の処理が行われずに済みます。


別の例として、あるフォームに、検索の実行を起動するように構成されているボタンを使用して文字列の検索に使用される入力コンポーネント、および日付の送信に使用される関連付けられたボタンを使用して日付の入力に使用される別の入力テキスト・コンポーネントが設定されているとします。この例では、検索入力コンポーネントとボタンの両方をimmediateに設定します。これにより、無効な文字列が日付フィールドに入力された場合でも、日付入力コンポーネントのコンバータが起動されないため、ユーザーは検索を実行できます。また、検索入力テキストはimmediateに設定され、日付入力フィールドは設定されないため、検索入力テキストのみ処理されます。両方のフィールドは同じフォーム内にあるため、ユーザーが日付フィールドに有効な日付を入力し、その後、検索を実行して「保存」ボタンをクリックしない場合、検索結果が表示されるときに、入力した値が引き続き表示されます。例5-2に、2つのフィールドと2つのボタンに使用されるコードを示します。

例5-2 immediateを使用する入力コンポーネントとコマンド・コンポーネント

<af:form>
  <af:inputText immediate="true" label="Search"  value="#{mybean.search}"
                valueChangeListener="#{mybean.searchValueChangeListener}"/>
  <af:button immediate="true" text="search"
                    actionListener="#{mybean.searchActionListener}"/>
  [.... tags to render search result ....]
 
  <af:inputText label="Date" value="#{mybean.date}"
                valueChangeListener="#{mybean.valueChangeListener}">
    <af:convertDateTime dateStyle="long"/>
  </af:inputText>
  <af:button text="save"  actionListener="#{mybean.actionListener}"/>
</af:form>

図5-5に、ユーザーが次のことを行う場合のこのページのライフサイクルを示します。

図5-5 コマンド・コンポーネントと入力コンポーネント両方のimmediate属性

入力とコマンドの両方でimmediateを使用する

同じページのeditableValueHolderおよびactionSourceコンポーネントにimmediate属性を使用する場合、次の点に注意する必要があります。

5.2.1 immediate属性の使用方法

始める前に

immediate属性に関する知識が役立つ場合があります。詳細は、5.2項「immediate属性の使用」を参照してください。

immediate属性を使用する手順:

  1. JSFページで、immediateにするコンポーネントを選択します。

  2. 「プロパティ」ウィンドウで、「動作」セクションを開き、immediate属性をtrueに設定します。

5.3 最適化されたライフサイクルの使用

ADF Facesには、ページの特定のコンポーネントにのみJSFページ・リクエスト・ライフサイクル(変換と検証を含む)を実行する最適化されたライフサイクルが用意されています。この部分ページ・ライフサイクルは、部分ページ・レンダリング(PPR)と呼ばれます。特定のADF Facesコンポーネントはイベント・ルート・コンポーネントとして見なされ、最適化されたライフサイクルが実行される境界を決定します。イベント・ルート・コンポーネントは、2通りの方法で決定できます。

イベント・ルートで実行されているコンポーネントおよびその子コンポーネントとは別に、イベント・ルート階層外部の他のコンポーネントを最適化されたライフサイクルに参加するように宣言して構成できます。コンポーネントの特定のイベントに限って最適化されたライフサイクルをトリガーさせたり、実際に実行されるコンポーネントと単にリフレッシュされるだけのコンポーネントを設定することもできます。

ADF FacesフレームワークでのPPRの使用方法とアプリケーションでのPPRの使用方法の詳細は、第8章「部分ページ・コンテンツの再レンダリング」を参照してください。

5.4 クライアント側ライフサイクルの使用

ADF Facesフレームワークは、クライアント側変換および検証を提供します。サーバーにアクセスせずにページで実行できる独自のJavaScriptベースのコンバータおよびバリデータを作成できます。

クライアント側検証を使用して、特定のクライアント・イベントがキューに入れられたときに、適切なフォームまたはサブフォームのクライアント検証をトリガーできます(サブフォームの詳細は、5.5項「サブフォームを使用したページでのセクションの作成」を参照してください)。このクライアント検証が失敗し、既知のエラーがあることがわかった場合は、通常サーバーに伝播するイベント(たとえば、フォームが送信されたときのボタンのactionEvent)はサーバーに移動しません。イベントが配信されないということは、なにも送信されないということでもあり、クライアント・リスナーはコールされません。これは、サーバーで検証が失敗した場合にライフサイクルがレスポンスのレンダリング・フェーズにジャンプするサーバー側検証に似ています。アクション・イベントはキューに入れられますが、決して配信されず、actionListenerハンドラ・メソッドは決してコールされません。

たとえば、ADF Facesで入力コンポーネントにrequired属性を用意し、この検証がクライアントで実行されるとします。この属性をtrueに設定すると、コンポーネントの値がnullの場合、フレームワークでページにエラーが表示され、サーバーへの送信は必要ありません。例5-3に、inputTextコンポーネントのrequired属性をtrueに設定し、actionListener属性がマネージドBeanのメソッドにバインドされているボタンを含むコードを示します。

例5-3 簡単なクライアント側検証の例

<af:form>
  <af:inputText id="input1" required="true" value="a"/>
  <af:button text="Search" actionListener="#{demoForm.search}"/>
</af:form>

このページを実行した場合、inputTextコンポーネントの値のフィールドをクリアし、フィールドからタブ移動すると、フィールドが赤い縁取りで再表示されます。フィールドをクリックすると、図5-6に示すように、値が必要なことを示すエラー・メッセージが表示されます。サーバーへの送信はなく、エラーの検出とメッセージの生成はすべてクライアントで行われます。

図5-6 クライアント側検証でサーバーへの送信なしにエラーを表示

必須属性のクライアント側検証

この例で、値のフィールドをクリアし、「検索」ボタンをクリックした場合、必須フィールドが空でありエラーが発生するため、ページは送信されません。アクション・イベントは配信されず、アクション・リスナーにバインドされたメソッドは実行されません。サーバーで検証が失敗したことをクライアントが通知できる場合はページを送信する理由がないため、このプロセスは適切です。

クライアント側の検証と変換の詳細は、第7章「入力の検証および変換」を参照してください。

5.5 サブフォームを使用したページでのセクションの作成

JSF参照実装では、ページのセクションを別個に送信する場合、複数のフォームを使用する必要があります。ただし、複数のフォームにはページの状態の複数のコピーが必要なため、送信されないフォームでのユーザーの編集内容が失われる可能性があります。

ADF Facesには、別個に送信可能なページのセクションを示すサブフォーム・コンポーネントのサポートが追加されています。サブフォームのコンテンツは、サブフォーム内部のコンポーネントがページの送信に関与する場合にのみ検証(または処理)されるため、まったく別のフォーム要素を使用するという妥協をすることなく、検証されてモデルへプッシュされるコンポーネントのセットを比較的きめ細かく制御できます。サブフォームを使用するページの送信時に、ページの状態が一度だけ書き込まれ、すべてのユーザー編集内容が保持されます。


ベスト・プラクティス:

常にページごとに1つのみのformタグを使用します。複数のformタグを使用する必要がある場合は、subformタグを使用します。


サブフォームでは、ページがそのサブフォーム外のコンポーネントによって送信された場合でも、リクエスト値の適用フェーズでその子コンポーネントを常に実行できます。ただし、検証の処理フェーズとモデル値の更新フェーズはスキップされます(これは、送信されない場合はリクエスト値の適用フェーズを実行できない通常のフォーム・コンポーネントとは異なります)。サブフォーム外のコンポーネントが送信アクションの原因となった場合にサブフォーム内のコンポーネントを検証の処理フェーズとモデル値の更新フェーズで処理できるようにするには、default属性を使用します。サブフォームのdefault属性をtrueに設定すると、そのサブフォームはほとんどの点で他のサブフォームと同様に動作しますが、ページのどのサブフォームにもその子コンポーネントからの適切なイベントがない場合、defaulttrueに設定されたサブフォームは、その子コンポーネントの1つが送信の原因となったかのように動作します。サブフォームの詳細は、11.2項「formの定義」を参照してください。

5.6 オブジェクト・スコープ・ライフサイクル

実行時、ページがデータにアクセスできるオブジェクト・スコープに必要なデータを格納することで、データをページに渡します。スコープによってオブジェクトの存続期間が決まります。オブジェクトをスコープに置くと、EL式を使用してスコープからアクセスできます。たとえば、fooという名前のマネージドBeanを作成し、Beanをリクエスト・スコープに存続させるよう定義するとします。このBeanにアクセスするには、#{requestScope.foo}という式を使用します。

標準JSFアプリケーションには、次の5種類のスコープがあります。

標準JSFスコープに加え、ADF Facesには次のスコープが用意されています。


注意:

これらは標準JSFスコープではないため、EL式には、Beanを参照するためのスコープを明示的に含める必要があります。たとえば、pageFlowScopeスコープからMyBeanマネージドBeanを参照するには、#{pageFlowScope.MyBean}という式になります。


オブジェクト・スコープは、プログラミング言語のグローバル変数およびローカル変数のスコープに相当します。スコープが広いほど、オブジェクトの可用性が高くなります。存続期間の間、これらのオブジェクトで特定のインタフェースの公開、情報の保持および他のオブジェクトへの変数とパラメータの受渡しを行うことができます。たとえば、sessionScopeスコープに定義されているマネージドBeanは、複数のページ・リクエストの間使用できます。しかし、requestScopeスコープに定義されているマネージドBeanは、1つのページ・リクエストの間のみ使用できます。

図5-7に、各タイプのスコープが有効な期間およびページ・フローとの関係を示します。

図5-7 スコープとページ・フローの関係

ADFライフサイクルでのスコープ

マネージドBeanを登録するスコープまたは値を格納するスコープを決める場合、可能なかぎり狭いスコープを常に使用するようにします。sessionScopeスコープは、ユーザー情報やコンテキスト情報などのセッション全体に関係する情報にのみ使用します。あるページから別のページへの値の受渡しには、sessionScopeスコープは使用しないようにします。


注意:

フルFusionテクノロジ・スタックを使用している場合、様々な構成ファイルにマネージドBeanを登録するためのオプションがあります。詳細は、『Oracle Application Development FrameworkによるFusion Webアプリケーションの開発』のFusion WebアプリケーションでのマネージドBeanの使用に関する項を参照してください。


5.7 ページ間の値の受渡し


注意:

完全なFusionテクノロジ・スタックを使用していて、ADFバインド・タスク・フローのページ間またはADFリージョンとページの間での値の受渡しについての情報が必要な場合は、『Oracle Application Development FrameworkによるFusion Webアプリケーションの開発』の「ADFタスク・フローの概説」の章を参照してください。


ADF FacesのpageFlowScopeスコープは、ページ間での値の受渡しを容易にするため、マスター/ディテール・ページを簡単に開発できます。pageFlowScopeスコープに追加された値は、redirectディレクティブを使用する場合でも、ユーザーがページ間を移動する際に自動的に継続使用できます。ただし、sessionスコープとは異なり、これらの値を表示できるのは、現在のページ・フローまたはプロセス内のみです。ユーザーが新たなウィンドウを開いてナビゲートを始めた場合、そのウィンドウは固有のプロセスを持ちます。各ウィンドウに格納された値は独立したままです。

標準JSFスコープに格納されているオブジェクト同様、pageFlowスコープに格納されているオブジェクトもEL式を介してアクセスできます。pageFlowスコープとの唯一の違いは、オブジェクト名にpageFlowScope接頭辞を使用する必要があることです。たとえば、ボタンのラベルをpageFlowスコープに格納されているマネージドBeanで指定し、ボタンが選択されるとBeanのメソッドがコールされるようにする場合は、ページに次のコードを使用します。

<af:button text="#{pageFlowScope.buttonBean.label}"
                  action="#{pageFlowScope.buttonBean.action}"/>

pageFlowScopeは、Javaコードからアクセスできるjava.util.Mapオブジェクトです。setPropertyListenerタグでは、スコープにプロパティ値を設定でき、タグでリスニングするイベントを定義することもできます。たとえば、type属性をactionに設定したsetPropertyListenerタグを使用すると、ナビゲーション前にアクション・ソース(buttonなど)で値を設定する宣言的方法が提供されます。pageFlowScopeスコープとsetPropertyListenerタグを併用して、バッキングBeanにJavaコードを記述せずに、ページ間で値を受渡しできます。たとえば、setPropertyListenerタグとコマンド・コンポーネントを使用してpageFlowScopeスコープに値を設定するページと、テキスト・コンポーネントでpageFlowScopeスコープを使用して値を取得する別のページがある場合があります。

pageFlowScopeスコープを使用して、ダイアログなどの2つ目のウィンドウ間で値を設定することもできます。buttonコンポーネントなどから2つ目のウィンドウを起動する場合、launchEventイベントおよびpageFlowScopeスコープを使用して、親プロセスの値をオーバーライドせずに2つ目のウィンドウとの間で値を受け渡すことができます。

5.7.1 Javaコード内でのpageFlowScopeスコープの使用方法

pageFlowスコープは、アプリケーションのJavaコード内からアクセスできます。終了したら、スコープを必ずクリアしてください。


注意:

アプリケーションでADFコントローラが使用されている場合は、スコープを手動でクリアする必要はありません。


始める前に

オブジェクト・スコープに関する知識が役立つ場合があります。詳細は、5.6項「オブジェクト・スコープ・ライフサイクル」を参照してください。pageFlowスコープを使用して値を渡す方法も理解しておく必要があります。詳細は、5.7項「ページ間の値の受渡し」を参照してください。

JavaコードでpageFlowScopeを使用する手順:

  1. pageFlowScopeスコープへの参照を得るために、org.apache.myfaces.trinidad.context.RequestContext.
    getPageFlowScope()
    メソッドを使用します。

    たとえば、pageFlowScopeスコープからオブジェクトを取得するには、次のJavaコードを使用します。

    import java.util.Map;
    import org.apache.myfaces.trinidad.context.RequestContext;
    . . .
    Map pageFlowScope = RequestContext.getCurrentInstance().getPageFlowScope();
    Object myObject = pageFlowScope.get("myObjectName");
    
  2. pageFlowScopeスコープをクリアするには、スコープにアクセスしてから手動でクリアします。

    たとえば、次のJavaコードを使用してスコープをクリアします。

    RequestContext afContext = RequestContext.getCurrentInstance();
    afContext.getPageFlowScope().clear();
    

5.7.2 Javaコードを記述せずにpageFlowScopeスコープを使用する方法

Javaコードを記述せずにpageFlowScopeスコープを使用するには、setPropertyListenerタグとコマンド・コンポーネントを組み合せて使用し、スコープに値を設定します。setPropertyListenerタグでは、リスニングするイベント・タイプを定義するtype属性を使用します。このタイプに合致しないイベントはすべて無視されます。設定後は、ページ・フロー内の別のページからその値にアクセスできます。


ヒント:

(以前のバージョンのADF Facesで使用されていた可能性がある)setActionListenerタグを使用するかわりに、setPropertyListenerタグを使用し、イベント・タイプをactionに設定します。


pageFlowScopeスコープに値を設定する手順:

  1. 値を設定するページ上で、「コンポーネント」ウィンドウを使用してコマンド・コンポーネントを作成します。コマンド・コンポーネントの作成方法は、20.3項「ボタンおよびリンクのナビゲーション目的での使用」を参照してください。

  2. 「コンポーネント」ウィンドウで、「操作」パネルの「リスナー」グループから、「プロパティ・リスナーの設定」をドラッグしてコマンド・コンポーネントに子としてドロップします。

    または、コンポーネントを右クリックし、「ボタンの中に挿入」→「ADF Faces」→「setPropertyListener」の順に選択します。

  3. 「プロパティ・リスナーの設定の挿入」ダイアログで、「自」フィールドに別のコンポーネントに設定される値を設定します。

    たとえば、従業員の名前を格納するMyBeanという名前のマネージドBeanがあり、次のページに値を受け渡すとします。「自」フィールドに#{myBean.empName}と入力します。

  4. 「至」フィールドにpageFlowScopeスコープでの値を設定します。

    たとえば、「至」フィールドに#{pageFlowScope.empName}と入力します。

  5. 「タイプ」ドロップダウン・メニューから、「アクション」を選択します。

    これによって、リスナーで、コマンド・コンポーネントに関連付けられているアクション・イベントをリスニングできます。

pageFlowScopeスコープから値にアクセスする手順:

  1. 値のアクセス元のページに、値を表示するコンポーネントをドロップします。

  2. コンポーネントの値を、setPropertyListenerタグで設定したTo値と同じ値に設定します。

    たとえば、outputTextコンポーネントで従業員名にアクセスするには、このコンポーネントの値を#{pageFlowScope.empName}に設定します。

5.7.3 実行時に行われる処理: 値の受渡し方法

setPropertyListenerタグを含むボタンをユーザーがクリックすると、リスナーが実行され、To値が解決されて取得され、その後、プロパティとしてpageFlowScopeスコープに格納されます。EL式を通じてそのプロパティにアクセスする後続のページでは、式は元のページによって設定された値に解決されます。