| Oracle® Fusion Middleware Oracle Application Development Framework Webユーザー・インタフェース開発者ガイド 11gリリース1 (11.1.1.7.0) B52029-07 |
|
![]() 前 |
![]() 次 |
この章では、JSFページ・リクエスト・ライフサイクル、ADF Facesのライフサイクルの追加機能、およびアプリケーションでのライフサイクルの最適な使用方法について説明します。
この章では、次の項目について説明します。
ADF Facesリッチ・クライアント・フレームワーク(RCF)はJSFフレームワークの拡張であるため、ADF Facesリッチ・クライアント・フレームワークを使用して構築したアプリケーションでは標準JSFページ・リクエスト・ライフサイクルが使用されます。ただし、ADF Facesフレームワークではライフサイクルが拡張され、クライアント側の値のライフサイクル、1つのページに複数のフォームを使用する弊害(ユーザーの編集内容の消失など)なしに独立して送信可能な領域をページに作成できるサブフォーム・コンポーネント、追加スコープなどの追加機能を提供します。
RCFで提供されるライフサイクルの拡張を十分に理解するには、標準JSFライフサイクルを理解することが重要です。この項では概要のみを説明します。詳細は、http://www.oracle.com/technetwork/java/index.htmlでJSFの仕様を参照してください。
JSFページが送信され、新しいページがリクエストされると、JSFページ・リクエスト・ライフサイクルが起動されます。このライフサイクルでは、ページの値の送信、現在のページでのコンポーネントの検証、ナビゲーション、コンポーネントの結果ページへの表示および状態の保存とリストアが処理されます。JSFライフサイクル・フェーズでは、UIコンポーネント・ツリーを使用してfacesコンポーネントの表示を管理します。このツリーはJSFページのランタイム表現で、ページ内の各UIコンポーネント・タグがツリーのUIコンポーネント・インスタンスに対応します。FacesServletオブジェクトで、JSFアプリケーションのページ・リクエスト・ライフサイクルが管理されます。FacesServletオブジェクトで、リクエスト処理に必要な情報を含み、ライフサイクルを実行するオブジェクトを起動するFacesContextと呼ばれるオブジェクトが作成されます。
図4-1に、ページ・リクエストのJSFライフサイクルを示します。ここに示すとおり、各フェーズの前後でイベントが処理されます。
JSFアプリケーションでは、ページ・リクエスト・ライフサイクルは次のようになります。
ビューのリストア:コンポーネント・ツリーが構築されます。これが初めてのレンダリングでない場合(ページがサーバーに送り返された場合)、適切な状態でツリーがリストアされます。これが初めてのレンダリングの場合、コンポーネント・ツリーが作成され、レスポンスのレンダリング・フェーズに移ります。
リクエスト値の適用: ツリーの各コンポーネントでリクエスト・パラメータから新規の値が(decodeメソッドを使用して)抽出され、ローカルに格納されます。関連するイベントの大半が、後の処理用にキューイングされます。コンポーネントのimmediate属性がtrueに設定されている場合、検証、変換、およびコンポーネントに関連付けられているイベントがこのフェーズで処理されます。詳細は、4.2項「immediate属性の使用」を参照してください。
検証の処理: コンポーネントのローカル値が、入力タイプから基礎のデータ型に変換されます。コンバータが失敗した場合、このフェーズが続行されて完了します(残りのすべてのコンバータ、バリデータおよび必要なチェックが実行されます)が、完了時にライフサイクルがレスポンスのレンダリング・フェーズに移ります。
失敗がない場合、コンポーネントのrequired属性が確認されます。値がtrueで、関連付けられているフィールドに値が含まれている場合、関連付けられているバリデータが実行されます。値がtrueで、フィールド値がない場合、このフェーズは完了します(残りのすべてのバリデータが実行されます)が、ライフサイクルがレスポンスのレンダリング・フェーズに移ります。値がfalseの場合、値が入力されていないかぎり(この場合は検証が実行されない)、フェーズが完了します。変換と検証の詳細は、第6章「入力の検証および変換」を参照してください。
このフェーズの最後に、変換後のローカル値が設定され、検証および変換のエラー・メッセージとイベントがFacesContextにキューイングされ、値変更イベントが配信されます。
|
ヒント: 要約すると、編集可能な入力コンポーネントの検証の処理フェーズでの手順は次のとおりです。
|
モデル値の更新: コンポーネントの検証済のローカル値がモデルに移され、ローカル・コピーが破棄されます。
アプリケーションの起動: アプリケーション・レベルのロジック(イベント・ハンドラなど)が実行されます。
レスポンスのレンダリング: ツリー内のコンポーネントがレンダリングされます。後続のリクエストおよびビューのリストア・フェーズ用に状態情報が保存されます。
ライフサイクルの説明の一環として、ユーザーが日付を入力し、コマンド・ボタンをクリックして入力値を送信する単純な入力テキスト・コンポーネントを持つページを考えてみます。valueChangeListenerメソッドもコンポーネントに登録されています。例4-1に、この例のコードを示します。
例4-1 JSFライフサイクルを説明するためのサンプル・コード
<af:form>
<af:inputText value="#{mybean.date}"
valueChangeListener="#{mybean.valueChangeListener}">
<af:convertDateTime dateStyle="long"/>
</af:inputText>
<af:commandButton text="Save" actionListener="#{mybean.actionListener}"/>
</af:form>
ユーザーが文字列「June 25, 2005」を入力し、送信ボタンをクリックするとします。図4-2に、値がライフサイクルでどのように受け渡され、各イベントがどの時点で処理されるかを示します。
immediate属性を使用して、ライフサイクルのリクエスト値の適用フェーズまでコンポーネントの処理を進めることができます。actionSourceコンポーネント(commandButtonなど)がimmediateに設定されている場合、アプリケーションの起動フェーズではなく、リクエスト値の適用フェーズでイベントが配信されます。actionListenerハンドラでレスポンスのレンダリング・フェーズがコールされ、検証およびモデルの更新フェーズが省略されます。
たとえば、「取消」ボタンをimmediateとして構成し、前のページに戻るために使用される文字列をアクションで返すとします(ナビゲーションの詳細は、第18章「ナビゲーション・コンポーネントの使用」を参照)。「取消」ボタンはimmediateに設定されているため、ユーザーが「取消」ボタンをクリックすると、すべての検証が省略され、入力されたデータはモデルに更新されず、図4-3に示されているように、ユーザーは見込みどおりにナビゲートされます。
|
注意: ナビゲーションを提供せず、 |
コマンド・コンポーネントと同様、表示イベントを起動するコンポーネント(showDetailコンポーネントなど)、およびeditableValueHolderコンポーネント(inputTextコンポーネントなどの変更可能な値を保持するコンポーネント)の場合、イベントはリクエスト値の適用フェーズに送られます。ただし、editableValueHolderコンポーネントの場合、フェーズを省略するかわりに、変換、検証およびvalueChangeEventsイベントの配信が、プロセスの検証フェーズの後ではなく、ライフサイクルの前半のリクエスト値の適用フェーズで行われます。ライフサイクルのフェーズは省略されません。
図4-4に、immediate属性がtrueに設定されている入力コンポーネントのライフサイクルを示します。入力コンポーネントは文字列として入力された日付を取得し、コマンド・ボタンがクリックされたときにDateオブジェクトとしてその日付を格納します。
入力コンポーネントのimmediate属性をtrueを設定すると、1つ以上の入力コンポーネントをその他のコンポーネントより前に検証する必要がある場合に役立ちます。これらのコンポーネントの1つで無効なデータが検出されると、同じページの他の入力コンポーネントの検証が省略されるため、ページに表示されるエラー・メッセージの数が削減されます。
|
パフォーマンスのヒント:
|
別の例として、あるフォームに、検索の実行を起動するように構成されているコマンド・ボタンを使用して文字列の検索に使用される入力コンポーネント、および日付の送信に使用される関連付けられたコマンド・ボタンを使用して日付の入力に使用される別の入力テキスト・コンポーネントが設定されているとします。この例では、検索入力コンポーネントとボタンの両方をimmediateに設定します。これにより、日付入力コンポーネントのコンバータが起動されないため、日付フィールドに無効な文字列が入力されている場合でも、ユーザーは検索を実行できます。また、検索入力テキストがimmediateに設定され、日付入力フィールドでは設定されてないため、検索入力テキストのみ処理されます。両方のフィールドは同じフォーム内にあるため、ユーザーが日付フィールドに有効な日付を入力し、その後、検索を実行して「保存」ボタンをクリックしない場合、検索結果が表示されるときに、入力した値が引き続き表示されます。例4-2に、2つのフィールドと2つのボタンに使用されるコードを示します。
例4-2 immediateを使用する入力コンポーネントとコマンド・コンポーネント
<af:form>
<af:inputText immediate="true" label="Search" value="#{mybean.search}"
valueChangeListener="#{mybean.searchValueChangeListener}"/>
<af:commandButton 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:commandButton text="save" actionListener="#{mybean.actionListener}"/>
</af:form>
図4-5に、ユーザーが次のことを行う場合のこのページのライフサイクルを示します。
binkyを「日付」入力フィールドに入力する(これは有効なエントリではありません)。
dressを「検索」フィールドに入力する。
「検索」ボタンをクリックして、dressの検索を実行する。
「保存」ボタンをクリックして、値binkyを日付として保存する。
同じページのeditableValueHolderおよびactionSourceコンポーネントにimmediate属性を使用する場合、次の点に注意する必要があります。
editableValueHolderコンポーネントがimmediateとマークされている場合、モデル値の更新フェーズの前に実行されます。immediateなactionSourceコンポーネントにeditableValueHolderコンポーネントからのデータが必要な場合、editableValueHolderコンポーネントに入力されたデータはモデルの更新フェーズまでモデルで使用できないため、これが問題となる場合もあります。immediateなactionSourceコンポーネントがあり、このコンポーネントでデータが必要な場合、editableValueHolderフィールドのimmediateも設定します。こうすることで、editableValueHolderコンポーネントのgetValueメソッドをコールでき、ローカル値が返されます。モデルにはまだプッシュされていませんが、コンポーネントで使用できます。
immediateなeditableValueHolderコンポーネントが検証に失敗した場合でも、immediateなactionSourceコンポーネントは実行されます。
immediate属性を使用する手順:
JSFページで、immediateにするコンポーネントを選択します。
プロパティ・インスペクタで、「動作」セクションを開き、immediate属性をtrueに設定します。
ADF Facesには、ページの特定のコンポーネントでのみJSFページ・リクエスト・ライフサイクル(変換と検証を含む)を実行する場合に使用できる最適化されたライフサイクルが用意されています。たとえば、必須属性がtrueに設定されたinputTextコンポーネントがページにあるとします。同じページにラジオ・ボタンがあり、選択すると、図4-6に示すようにoutputTextコンポーネントのテキストをページに表示または非表示します。
また、フィールドに必須テキストを入力する前に、ユーザーがラジオ・ボタンを選択できるとします。ラジオ・ボタン・コンポーネントで送信アクションを自動的にトリガーするよう設定し、immediate属性をtrueに設定して、inputTextコンポーネントの前に処理されるようにできますが、valueChangeEventリスナーを追加し、ここでレスポンスのレンダリング・フェーズをコールして、入力テキスト・コンポーネントで検証が実行されないようにする必要もあります。
このコードをリスナーに記述するかわりに、ADF Facesでは、ページに境界を設定してライフサイクルが境界内のコンポーネントでのみ実行されるようにすることができます。境界を判断するために、処理するコンポーネントのルートをフレームワークに通知する必要があります。このコンポーネントは、次の2つの方法で決めることができます。
コンポーネント: リージョンは、フレームワークで境界が認識されるコンポーネントの1つの例です。リージョン内でトリガーされたイベントに関係なく、リージョン外のコンポーネントでライフサイクルは実行されません。
イベント: 特定のイベントはルートであるコンポーネントを示します。たとえば、showDetailコンポーネントを展開または縮小(8.9項「コンテンツの動的な表示および非表示」を参照)すると送信される表示イベントは、showDetailコンポーネントがルートであることを示しすため、showDetailコンポーネントおよび子コンポーネントでのみライフサイクルが実行されます。ライフサイクルは、その表示イベントをリスニングするよう構成されているコンポーネントでも実行できます。ルート・コンポーネントのイベントをリスニングするようコンポーネントを構成して処理することは、クロスコンポーネント・リフレッシュと呼ばれます。
クロスコンポーネント・リフレッシュでは、あるコンポーネントからのイベントが別のコンポーネント(ターゲットと呼ばれる)のトリガーとして機能するように依存性を設定できます。トリガー・コンポーネントでイベントが発生すると、ターゲット・コンポーネントと、トリガーおよびターゲットの両方の子コンポーネントでライフサイクルが実行され、これらのコンポーネントのみが再レンダリングされます。これは、部分ページ・レンダリング(PPR)とみなされます。
ラジオ・ボタンの例では、例4-3に示すように、ラジオ・ボタンをトリガーとし、出力テキストを含むpanelGroupLayoutコンポーネントをターゲットとして設定します。
例4-3 クロスコンポーネント・レンダリングの例
<af:form> <af:inputText label="Required Field" required="true"/> <af:selectBooleanRadio id="show" autoSubmit="true" text="Show" value="#{validate.show}"/> <af:selectBooleanRadio id="hide" autoSubmit="true" text="Hide" value="#{validate.hide}"/> <af:panelGroupLayout partialTriggers="show hide" id="panel"> <af:outputText value="You can see me!" rendered="#{validate.show}"/> </af:panelGroupLayout> </af:form>
ラジオ・ボタンのautoSubmit属性がtrueに設定されているため、ボタンが選択されると、SelectionEventが起動され、ラジオ・ボタンがルートとみなされます。panelGroupLayoutコンポーネントが両方のラジオ・コンポーネントのターゲットとして設定されているため、イベントが起動されると、selectOneRadio(ルート)、panelGroupLayoutコンポーネント(ルートのターゲット)とその子コンポーネント(outputTextコンポーネント)のみライフサイクルの処理が行われます。outputTextコンポーネントは表示ラジオ・ボタンの選択時にのみレンダリングされるよう構成されているため、ユーザーは、ラジオ・ボタンの上の必須入力フィールドにテキストを入力しなくても、ラジオ・ボタンを選択して出力テキストを表示できます。
ADF FacesフレームワークでのPPRの使用方法とアプリケーションでのPPRの使用方法の詳細は、第7章「部分ページ・コンテンツの再レンダリング」を参照してください。
PPRで特定のコンポーネントの検証を回避できない場合があります。たとえば、例4-4に示すように、panelGroupLayoutコンポーネント内にoutputTextコンポーネントのかわりにinputTextコンポーネントを使用し、このrequired属性をtrueに設定するとします。
例4-4 クロスコンポーネントPPRで検証されるpanelGroupコンポーネント内のinputTextコンポーネント
<af:form>
<af:selectBooleanRadio id="show2" autoSubmit="true" text="Show"
value="#{validate.show2}"/>
<af:selectBooleanRadio id="hide2" autoSubmit="true" text="Hide"
value="#{validate.hide2}"/>
<af:panelGroupLayout partialTriggers="show2 hide2">
<af:inputText label="Required Field" required="true"
rendered="#{validate.show2}"/>
</af:panelGroupLayout>
</af:form>
この例では、ライフサイクルはルート(selectOneRadioコンポーネント)、ターゲット(panelGroupLayoutコンポーネント)とターゲットの子(inputTextコンポーネント)で実行されるため、inputTextコンポーネントは検証されます。inputTextコンポーネントが必須としてマークされているため、値がないと、検証が失敗し、エラーがスローされます。エラーのため、ライフサイクルはレスポンスのレンダリング・フェーズに移り、モデルは更新されません。このため、ラジオ・ボタンの値が更新されず、panelGroupLayoutコンポーネントは表示または非表示にできません。
このような場合、ラジオ・ボタンのimmediate属性を使用して検証を省略できます。これにより、inputTextコンポーネントのプロセスの検証フェーズの前にボタンのvalueChangeEventが実行されます。レスポンスのレンダリング・フェーズをコール(その結果、入力コンポーネントの検証を省略)するvalueChangeListenerハンドラ・メソッドを追加し、ラジオ・ボタンと入力コンポーネントに値を設定する必要があります。例4-5に、これを行うJSFコードを示します。
例4-5 immediate属性とvalueChangeListenerの使用
<af:form>
<af:selectOneRadio immediate="true"
valueChangeListener="#{validate.toggle}"
id="show2" autoSubmit="true" text="Show"
value="#{validate.show2}"/>
<af:selectOneRadio id="hide2" autoSubmit="true" text="Hide"
value="#{validate.hide2}"/>
<af:panelGroupLayout partialTriggers="show2 hide2">
<af:inputText label="Required Field" required="true"
rendered="#{validate.show2}"/>
</af:panelGroupLayout>
</af:form>
例4-6に、valueChangeListenerのコードを示します。
inputListOfValuesおよびinputComboBoxListOfValuesコンポーネントでは、4.3.1項「immediate属性と最適化されたライフサイクルの使用について」に説明されている手順は機能しません。次に例を示します。
ユーザーが従業員名を選択するinputListOfValuesコンポーネントと、必須の属性がtrueに設定されているinputTextコンポーネントがあるとします(図4-7に示すように、従業員を選択すると、従業員のIDで更新されます)。
これを達成するには、例4-7に示すように、EmpnoフィールドにEnameフィールドが部分トリガーとして含まれるように設定します。
例4-7
<af:inputListOfValues label="Ename" id="lov0" value="#{validateLOV.ename}" autoSubmit="true" immediate="true" popupTitle="Search and Select: Ename" searchDesc="Choose a name" model="#{validateLOV.listOfValuesModel}" valueChangeListener="#{validateLOV.immediateValueChange}" validator="#{validateLOV.validate}"/> <af:inputText label="Empno" value="#{validateLOV.empno}" required="true" id="lovDependent01" partialTriggers="lov0"/>
4.3.1項「immediate属性と最適化されたライフサイクルの使用について」のラジオ・ボタンおよび入力コンポーネントの例と同様、ライフサイクルはルート(inputListOfValuesコンポーネント)とターゲット(inputTextコンポーネント)の両方で実行されるため、ユーザーが検索アイコンをクリックすると、inputTextコンポーネントが検証されます。図4-8に示すように、inputTextコンポーネントが必須としてマークされているため、値がないと、検証が失敗し、エラーがスローされます。
ただし、4.3.1項「immediate属性と最適化されたライフサイクルの使用について」で推奨されている解決策(LOVコンポーネントのimmediate属性をtrueに設定し、LOVでValueChangeリスナーを使用する)では、検証エラーは修正されません。LOVコンポーネントでは、ValueChangeEventがキューイングされるのは、LOVコンポーネントの値が変更された場合のみです。このため、ユーザーが検索アイコンをした時点で、ADF LaunchPopupEventはimmediate属性の値に関係なく、アプリケーションの起動フェーズで常にキューイングされるため、ユーザーが検索アイコンをクリックするときにimmediate属性をtrueに設定しても影響はありません。つまり、最適化されたライフサイクルは、ルートおよびターゲット・コンポーネントの両方で通常どおりに実行されるため、入力コンポーネントによって検証エラーがスローされます。
ユーザーが「値リスト」ポップアップから新しい値を選択すると、LOVコンポーネントによって2つのイベントがキューイングされます。1つはValueChangeEventで、コンポーネントの値の変更を通知します。2つ目は、アプリケーションの起動フェーズでキューイングされるReturnPopupEventで、アプリケーション・メソッドで選択を処理できます。これらのイベントは両方とも、LOVが予想どおり動作できるように発生する必要があります。
前述のように、LOVコンポーネントがValueChangeEventをキューイングするのは、ユーザーが新しい値を選択した場合のみです。LOVコンポーネントでimmediate属性をtrueに設定すると、このイベントはリクエスト値の適用フェーズでキューイングされ、新しい値が検証されます。また、LOVコンポーネントでValueChangeListenerメソッドを作成し、その実装でレスポンスのレンダリング・フェーズにジャンプして、入力コンポーネントの検証を回避すると、図4-9に示すように、選択した値はモデルにプッシュされず、アプリケーションの起動フェーズでReturnPopupListenerがコールされることもなく、ターゲット入力コンポーネントが新しい値で更新されることもありません。
LOVコンポーネントを入力コンポーネントの部分トリガーとして宣言的に設定して、ValueChangeListenerにメソッドを作成するかわりに、ValueChangeEventおよびReturnPopupEventの両方を同じリクエストの一部としてキューイングし、新しく選択した値でターゲット・フィールドをリフレッシュする必要があり、この問題を解決するには、ReturnPopupEventにリスナーを作成する必要があります。このリスナーでは、LOVの部分ターゲットとして入力コンポーネントをプログラムで設定する必要があります。ReturnPopupListenerメソッドが実行されるまで、入力コンポーネントはLOVのターゲットにならないため、LOVのimmediate属性をtrueに設定する必要はなく、このため、ライフサイクルが実行されないので、検証が失敗しますリスナー・メソッドはValueChangeEventではなく、ReturnPopupEventに使用されるため、両方のイベントをキューイングして、モデルを適切に更新できます。
例4-8に、LOVおよびコンポーネントで必要なページ・コードを示します。
例4-8
<af:inputListOfValues label="Ename" id="lov1"
value="#{validateLOV.ename}" autoSubmit="true"
returnPopupListener="#{validate.processReturnPopup}"
Title="Search and Select: Ename" searchDesc="Choose a name"
model="#{validateLOV.listOfValuesModel}"
validator="#{validateLOV.validate}"/>
<af:inputText label="Empno" value="#{validateLOV.empno}" required="true"
id="lovDependent1" binding="#{validate.lovDependent1}"/>
入力コンポーネントでは、そのbinding属性を使用して、インスタンスをバッキングBeanに保存し、リスナー・メソッドからインスタンスにアクセスできます。例4-9に示すように、リスナー・メソッドは入力コンポーネントにアクセスし、LOVの部分ターゲットとして設定します。
部分ページ・レンダリングのプログラムによる設定の詳細は、7.3項「部分ページ・レンダリングのプログラムによる有効化」を参照してください。
ADF Facesフレームワークでは、クライアント側の変換と検証が提供されます。サーバーに送信する必要なく、JavaScriptベースの独自のコンバータとバリデータを作成してページで実行できます。
クライアント側検証を使用できるため、特定のクライアント・イベントがキューイングされると、適切なフォームまたはサブフォームのクライアント検証がトリガーされます(サブフォームの詳細は、4.5項「サブフォームを使用したページでの領域の作成」を参照)。このクライアント検証が失敗した場合(既知のエラー)、通常サーバーに伝播されるイベント(フォームの送信時のコマンド・ボタンのactionEventなど)がサーバーに送られません。イベントが配信されないということは、なにも送信されないということでもあり、クライアント・リスナーはコールされません。これは、サーバーで検証が失敗すると、ライフサイクルがレスポンスのレンダリング・フェーズに移り、キューイングされるがアクション・イベントが配信されず、actionListenerハンドラ・メソッドがコールされないという意味では、サーバー側検証と同様です。
たとえば、ADF Facesで入力コンポーネントにrequired属性を用意し、この検証がクライアントで実行されるとします。この属性をtrueに設定すると、コンポーネントの値がnullの場合、フレームワークでページにエラーが表示され、サーバーへの送信は必要ありません。例4-10に、inputTextコンポーネントのrequired属性をtrueに設定し、actionListener属性がマネージドBeanのメソッドにバインドされているコマンド・ボタンを含むコードを示します。
例4-10 簡単なクライアント側検証の例
<af:form>
<af:inputText id="input1" required="true" value="a"/>
<af:commandButton text="Search" actionListener="#{demoForm.search}"/>
</af:form>
このページを実行した場合、inputTextコンポーネントの値のフィールドをクリアし、フィールドからタブ移動すると、フィールドが赤い縁取りで再表示されます。フィールドをクリックすると、図4-10に示すように、値が必要なことを示すエラー・メッセージが表示されます。サーバーへの送信はなく、エラーの検出とメッセージの生成はすべてクライアントで行われます。
この例では、値のフィールドをクリアし、「検索」ボタンをクリックすると、必須フィールドが空でエラーが発生するため、ページが送信されず、アクション・イベントも配信されず、アクション・リスナーにバインドされているメソッドは実行されません。検証がサーバーで失敗することがクライアントでわかっている場合は、ページを送信する必要はないため、この処理は必要です。
クライアント側の検証と変換の詳細は、第6章「入力の検証および変換」を参照してください。
JSF参照実装では、ページの領域を別個に送信する場合、複数のフォームを使用する必要があります。ただし、複数のフォームにはページの状態の複数のコピーが必要なため、送信されないフォームでのユーザーの編集内容が失われる可能性があります。
ADF Facesには、別個に送信可能なページの領域を示すサブフォーム・コンポーネントのサポートが追加されています。サブフォームのコンテンツは、サブフォーム内のコンポーネントでページの送信を行う場合にのみ、検証(または処理)されるため、別のフォーム要素を使用するという妥協をすることなく、検証され、モデルへプッシュされるコンポーネントをきめ細かく制御できます。サブフォームを使用するページの送信時に、ページの状態が一度だけ書き込まれ、すべてのユーザー編集内容が保持されます。
|
ベスト・プラクティス: 常にページごとに1つのみの |
サブフォームでは、リクエスト値の適用フェーズが子コンポーネントで常に(ページがサブフォーム外のコンポーネントで送信された場合でも)実行されます。ただし、検証の処理およびモデル値の更新フェーズは省略されます(これは、通常のフォーム・コンポーネントと異なり、送信されない場合、リクエスト値の適用フェーズは実行できません)。サブフォーム外のコンポーネントで送信アクションが行われたときにサブフォームのコンポーネントで検証の処理およびモデル値の更新フェーズを処理できるようにするには、default属性を使用します。サブフォームのdefault属性をtrueに設定すると、他のサブフォームとほぼ同様に動作しますが、ページのどのサブフォームでも適切なイベントが子コンポーネントで発生しない場合、defaultがtrueに設定されているサブフォームは、子コンポーネントの1つで送信が行われた場合と同様に動作します。サブフォームの詳細は、9.2項「formの定義」を参照してください。
実行時、ページがデータにアクセスできるオブジェクト・スコープに必要なデータを格納することで、データをページに渡します。スコープによってオブジェクトの存続期間が決まります。オブジェクトをスコープに置くと、EL式を使用してスコープからアクセスできます。たとえば、fooという名前のマネージドBeanを作成し、Beanをリクエスト・スコープに存続させるよう定義するとします。このBeanにアクセスするには、#{requestScope.foo}という式を使用します。
標準JSFアプリケーションには、次の3種類のスコープがあります。
applicationScope: オブジェクトは、アプリケーションの間使用できます。
sessionScope: オブジェクトは、セッションの間使用できます。
requestScope: オブジェクトは、HTTPリクエストが送信されてから、レスポンスがクライアントに返るまでの間使用できます。
標準JSFスコープに加え、ADF Facesには次のスコープが用意されています。
pageFlowScope: オブジェクトは、ユーザーがあるページから別のページへのナビゲートを続けるかぎり使用できます。ユーザーが新たなブラウザ・ウィンドウを開いてナビゲートを始めた場合、そのウィンドウで別のpageFlowScopeスコープを持ちます。
backingBeanScope: ページ・フラグメントと宣言コンポーネントのみに対するマネージドBeanに使用します。オブジェクトは、HTTPリクエストが送信されてから、レスポンスがクライアントに返るまでの間使用できます。このスコープは、複数のページ・フラグメントと宣言コンポーネントがページにある場合があり、値の競合を避けるために別々のスコープ・インスタンスに値を保持するために必要です。backingBeanScopeスコープは、ページ・フラグメントまたは宣言コンポーネントに作成されたすべてのマネージドBeanに使用します。
viewScope: オブジェクトは、現在のビューのIDが変更されるまで使用できます。viewScopeスコープを使用して特定のページの値を保持します。
|
注意: これらは標準JSFスコープではないため、EL式には、Beanを参照するためのスコープを明示的に含める必要があります。たとえば、 |
オブジェクト・スコープは、プログラミング言語のグローバル変数およびローカル変数のスコープに相当します。スコープが広いほど、オブジェクトの可用性が高くなります。存続期間の間、これらのオブジェクトで特定のインタフェースの公開、情報の保持および他のオブジェクトへの変数とパラメータの受渡しを行うことができます。たとえば、sessionScopeスコープに定義されているマネージドBeanは、複数のページ・リクエストの間使用できます。しかし、requestScopeスコープに定義されているマネージドBeanは、1つのページ・リクエストの間のみ使用できます。
図4-11に、各タイプのスコープが有効な期間およびページ・フローとの関係を示します。
マネージドBeanを登録するスコープまたは値を格納するスコープを決める場合、可能なかぎり狭いスコープを常に使用するようにします。sessionScopeスコープは、ユーザー情報やコンテキスト情報などのセッション全体に関係する情報にのみ使用します。あるページから別のページへの値の受渡しには、sessionScopeスコープは使用しないようにします。
|
注意: フルFusionテクノロジ・スタックを使用している場合、様々な構成ファイルにマネージドBeanを登録するためのオプションがあります。詳細は、『Oracle Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド』のFusion WebアプリケーションでのマネージドBeanの使用に関する項を参照してください。 |
|
注意: フルFusionテクノロジ・スタックを使用していて、ADFバインド・タスク・フローのページ間またはADFリージョンとページの間での値の受渡しについての情報が必要な場合は、『Oracle Fusion Middleware Oracle Application Development Framework Fusion開発者ガイド』の「ADFタスク・フローの概説」を参照してください。 |
ADF FacesのpageFlowScopeスコープを使用すると、あるページから別のページへの値の受渡しが簡単になるため、マスター/ディテール・ページをより簡単に開発できます。pageFlowScopeスコープに追加された値は、ユーザーがあるページから別のページにナビゲートしても、自動的に継続して使用可能になり、これは、redirectディレクティブを使用している場合にも当てはまります。ただし、sessionスコープとは異なり、これらの値を表示できるのは、現在のページ・フローまたはプロセス内のみです。ユーザーが新しいブラウザ・ウィンドウを開いてナビゲートを開始すると、その一連のウィンドウは独自のプロセスを持ちます。各ウィンドウに格納された値は独立したままです。
標準JSFスコープに格納されているオブジェクト同様、pageFlowスコープに格納されているオブジェクトもEL式を介してアクセスできます。pageFlowスコープとの唯一の違いは、オブジェクト名にpageFlowScope接頭辞を使用する必要があることです。たとえば、ボタンのラベルをpageFlowスコープに格納されているマネージドBeanで指定し、ボタンが選択されるとBeanのメソッドがコールされるようにする場合は、ページに次のコードを使用します。
<af:commandButton text="#{pageFlowScope.buttonBean.label}"
action="#{pageFlowScope.buttonBean.action}"/>
pageFlowScopeは、Javaコードからアクセスされるjava.util.Mapオブジェクトです。setPropertyListenerタグでスコープにプロパティ値を設定したり、タグがリスニングするイベントも定義できます。たとえば、type属性をactionに設定したsetPropertyListenerタグを使用すると、ナビゲーション前にアクション・ソース(commandButtonなど)で値を設定する宣言的方法が提供されます。pageFlowScopeスコープとsetPropertyListenerタグを組み合せて使用して、バッキングBeanにJavaコードを記述することなく、あるページから別のページへ値を受け渡すことができます。たとえば、setPropertyListenerタグとコマンド・コンポーネントを使用してpageFlowScopeスコープに値を設定するページと、テキスト・コンポーネントでpageFlowScopeスコープを使用して値を取得する別のページを持つことができます。
pageFlowScopeスコープを使用して、ダイアログなどの2つ目のウィンドウ間で値を設定することもできます。commandButtonコンポーネントなどから2つ目のウィンドウを起動する場合、launchEventイベントおよびpageFlowScopeスコープを使用して、親プロセスの値をオーバーライドせずに2つ目のウィンドウとの間で値を受け渡すことができます。
pageFlowスコープは、アプリケーションのJavaコード内からアクセスできます。終了したら、スコープを必ずクリアしてください。
|
注意: アプリケーションでADFコントローラが使用されている場合は、スコープを手動でクリアする必要はありません。 |
JavaコードでpageFlowScopeを使用する手順:
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");
pageFlowScopeスコープをクリアするには、スコープにアクセスしてから手動でクリアします。
たとえば、次のJavaコードを使用してスコープをクリアします。
RequestContext afContext = RequestContext.getCurrentInstance(); afContext.getPageFlowScope().clear();
Javaコードを記述せずにpageFlowScopeスコープを使用するには、setPropertyListenerタグとコマンド・コンポーネントを組み合せて使用し、スコープに値を設定します。setPropertyListenerタグでは、リスニングするイベント・タイプを定義するtype属性を使用します。このタイプに合致しないイベントはすべて無視されます。設定後は、ページ・フロー内の別のページからその値にアクセスできます。
|
ヒント: (以前のバージョンのADF Facesで使用されていた可能性がある) |
pageFlowScopeスコープに値を設定する手順:
値の設定元のページで、コンポーネント・パレットを使用してコマンド・コンポーネントを作成します。
コンポーネント・パレットの「操作」パネルから、「プロパティ・リスナーの設定」をコマンド・コンポーネントの子としてドラッグ・アンド・ドロップします。
または、コンポーネントを右クリックし、「ボタンの中に挿入」→「ADF Faces」→「setPropertyListener」を選択します。
「プロパティ・リスナーの設定の挿入」ダイアログで、「自」フィールドに別のコンポーネントに設定される値を設定します。
たとえば、従業員の名前を格納するMyBeanという名前のマネージドBeanがあり、次のページに値を受け渡すとします。「自」フィールドに#{myBean.empName}と入力します。
「至」フィールドにpageFlowScopeスコープでの値を設定します。
たとえば、「至」フィールドに#{pageFlowScope.empName}と入力します。
「タイプ」ドロップダウン・メニューから「アクション」を選択します。
これによって、リスナーで、コマンド・コンポーネントに関連付けられているアクション・イベントをリスニングできます。
pageFlowScopeスコープから値にアクセスする手順:
値のアクセス元のページに、値を表示するコンポーネントをドロップします。
コンポーネントの値を、setPropertyListenerタグで設定したTo値と同じ値に設定します。
たとえば、outputTextコンポーネントで従業員名にアクセスするには、このコンポーネントの値を#{pageFlowScope.empName}に設定します。