Oracle® Fusion Middleware Oracle Application Development Framework Webユーザー・インタフェース開発者ガイド 11gリリース2 (11.1.2.1.0) B66719-01 |
|
前 |
次 |
この章では、JSFページ・リクエストのライフサイクルとADF Facesからライフサイクルへの追加、およびアプリケーションでのライフサイクル・プロパティの使用方法について説明します。
この章では、次の項目について説明します。
ADF FacesフレームワークはJSFフレームワークの拡張であるため、ADF Facesフレームワークを使用して構築したアプリケーションでは標準JSFページ・リクエスト・ライフサイクルが使用されます。ただし、ADF Facesフレームワークではライフサイクルが拡張され、クライアント側の値のライフサイクル、1つのページに複数のフォームを使用する弊害(ユーザーの編集内容の消失など)なしに独立して送信可能なセクションをページに作成できるサブフォーム・コンポーネント、追加スコープなどの追加機能を提供します。
フレームワークによって提供されるライフサイクルの強化についてよりよく理解するには、標準のJSFライフサイクルについて理解することが重要です。この項の説明は概要にすぎません。詳細な説明は、http://www.jcp.org/en/jsr/detail?id=314
にあるJSFの仕様を参照してください。
JSFページが送信され、新しいページがリクエストされると、JSFページ・リクエスト・ライフサイクルが起動されます。このライフサイクルでは、ページの値の送信、現在のページのコンポーネントの検証、コンポーネントのナビゲーションと結果ページへの表示および状態の保存とリストアが処理されます。JSFライフサイクル・フェーズでは、UIコンポーネント・ツリーを使用してfacesコンポーネントの表示を管理します。このツリーはJSFページのランタイム表現で、ページ内の各UIコンポーネント・タグがツリーのUIコンポーネント・インスタンスに対応します。FacesServlet
オブジェクトで、JSFアプリケーションのページ・リクエスト・ライフサイクルが管理されます。FacesServlet
オブジェクトで、リクエスト処理に必要な情報を含み、ライフサイクルを実行するオブジェクトを起動するFacesContext
と呼ばれるオブジェクトが作成されます。
図5-1に、ページ・リクエストのJSFライフサイクルを示します。ここに示すとおり、各フェーズの前後でイベントが処理されます。
JSFアプリケーションでは、ページ・リクエスト・ライフサイクルは次のようになります。
ビューのリストア:コンポーネント・ツリーが構築されます。これが初めてのレンダリングでない場合(ページがサーバーに送り返された場合)、適切な状態でツリーがリストアされます。これが初めてのレンダリングの場合、コンポーネント・ツリーが作成され、レスポンスのレンダリング・フェーズに移ります。
リクエスト値の適用: ツリーの各コンポーネントでリクエスト・パラメータから新規の値が(decodeメソッドを使用して)抽出され、ローカルに格納されます。関連付けられているほとんどのイベントは、後の処理のためにキューされます。コンポーネントのimmediate
属性がtrue
に設定されている場合、検証、変換およびコンポーネントに関連付けられているイベントがこのフェーズで処理されます。詳細は、5.2項「immediate属性の使用」を参照してください。
検証の処理: コンポーネントのローカル値が、入力タイプから基礎のデータ型に変換されます。コンバータが失敗した場合、このフェーズが続行されて完了します(残りのすべてのコンバータ、バリデータおよび必要なチェックが実行されます)が、完了時にライフサイクルがレスポンスのレンダリング・フェーズに移ります。
失敗がない場合、コンポーネントのrequired
属性が確認されます。値がtrue
で、関連付けられているフィールドに値が含まれている場合、関連付けられているバリデータが実行されます。値がtrue
で、フィールド値がない場合、このフェーズは完了します(残りのすべてのバリデータが実行されます)が、ライフサイクルがレスポンスのレンダリング・フェーズに移ります。値がfalse
の場合、値が入力されていないかぎり(この場合は検証が実行されない)、フェーズが完了します。変換と検証の詳細は、第7章「入力の検証および変換」を参照してください。
このフェーズの最後に、変換後のローカル値が設定され、検証および変換のエラー・メッセージとイベントがFacesContext
にキューイングされ、値変更イベントが配信されます。
ヒント: 要約すると、編集可能な入力コンポーネントの検証の処理フェーズでの手順は次のとおりです。
|
モデル値の更新: 検証されたコンポーネントのローカル値がモデルに移動され、ローカル・コピーが破棄されます。
アプリケーションの起動: アプリケーション・レベルのロジック(イベント・ハンドラなど)が実行されます。
レスポンスのレンダリング: ツリー内のコンポーネントがレンダリングされます。後続のリクエストおよびビューのリストア・フェーズ用に状態情報が保存されます。
ライフサイクルの説明の一環として、ユーザーが日付を入力し、コマンド・ボタンをクリックして入力値を送信する単純な入力テキスト・コンポーネントを持つページを考えてみます。valueChangeListener
メソッドもコンポーネントに登録されています。例5-1に、この例のコードを示します。
例5-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」を入力し、送信ボタンをクリックするとします。図5-2に、値がライフサイクルでどのように受け渡され、各イベントがどの時点で処理されるかを示します。
immediate
属性を使用して、ライフサイクルのリクエスト値の適用フェーズまでコンポーネントの処理を進めることができます。actionSource
コンポーネント(commandButton
など)がimmediate
に設定されている場合、イベントはアプリケーションの起動フェーズではなくリクエスト値の適用フェーズで配信されます。actionListener
ハンドラは、レスポンスのレンダリング・フェーズをコールします。
たとえば、「取消」ボタンをimmediate
に構成し、アクションが前のページに戻るために使用される文字列を返すようにする場合があります(ナビゲーションの詳細は、第20章「ナビゲーション・コンポーネントの使用」を参照してください)。「取消」ボタンはimmediate
に設定されているため、ユーザーが「取消」ボタンをクリックすると、図5-3に示すように、すべての検証がスキップされ、入力されたデータはモデルに更新されず、ユーザーは期待どおりにナビゲートします。
注意: ナビゲーションを提供せず、immediate に設定されているコマンド・ボタンも、レスポンスのレンダリング・フェーズに直接移動します。検証、モデルの更新およびアプリケーションの起動のフェーズはスキップされるため、新しい値はサーバーにプッシュされません。 |
コマンド・コンポーネントと同様に、表示イベントを呼び出すコンポーネント(showDetail
コンポーネントなど)、およびeditableValueHolder
コンポーネント(inputText
コンポーネントなど、変更可能な値を保持するコンポーネント)では、イベントはリクエスト値の適用フェーズに配信されます。一方、editableValueHolder
コンポーネントでは、フェーズをスキップするかわりに、valueChangeEvents
イベントの変換、検証および配信が、検証の処理フェーズではなく、ライフサイクル初期のリクエスト値の適用フェーズで実行されます。スキップされるライフサイクル・フェーズはありません。
図5-4に、immediate
属性がtrue
に設定されている入力コンポーネントのライフサイクルを示します。入力コンポーネントは文字列として入力された日付を取得し、コマンド・ボタンがクリックされたときにDateオブジェクトとしてその日付を格納します。
入力コンポーネントのimmediate
属性をtrue
を設定すると、1つ以上の入力コンポーネントをその他のコンポーネントより前に検証する必要がある場合に役立ちます。これらのコンポーネントの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: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>
図5-5に、ユーザーが次のことを行う場合のこのページのライフサイクルを示します。
apple
を「日付」入力フィールドに入力する(これは有効なエントリではありません)。
orange
を「検索」フィールドに入力する。
「検索」ボタンをクリックして、orange
の検索を実行する。
「検索」ボタンをクリックして、値appleを日付として保存する。
同じページのeditableValueHolder
およびactionSource
コンポーネントにimmediate
属性を使用する場合、次の点に注意する必要があります。
editableValueHolder
コンポーネントがimmediate
とマークされている場合、モデル値の更新フェーズの前に実行されます。immediateなactionSource
コンポーネントにeditableValueHolder
コンポーネントからのデータが必要な場合、editableValueHolder
コンポーネントに入力されたデータはモデルの更新フェーズまでモデルで使用できないため、これが問題となる場合もあります。immediateなactionSource
コンポーネントがあり、このコンポーネントでデータが必要な場合、editableValueHolder
フィールドのimmediate
も設定します。こうすることで、editableValueHolder
コンポーネントのgetValue
メソッドをコールでき、ローカル値が返されます。モデルにはまだプッシュされていませんが、コンポーネントで使用できます。
immediateなeditableValueHolder
コンポーネントが検証に失敗した場合でも、immediateなactionSource
コンポーネントは実行されます。
前提作業
immediate
属性に関する知識が役立つ場合があります。詳細は、5.2項「immediate属性の使用」を参照してください。
immediate属性を使用する手順:
JSFページで、immediateにするコンポーネントを選択します。
プロパティ・インスペクタで、「動作」セクションを開き、immediate
属性をtrue
に設定します。
ADF Facesには、ページの特定のコンポーネントにのみJSFページ・リクエスト・ライフサイクル(変換と検証を含む)を実行する場合に使用できる最適化されたライフサイクルが用意されています。たとえば、必須属性がtrue
に設定されたinputText
コンポーネントがページにあるとします。同じページにラジオ・ボタンがあり、選択すると、図5-6に示すようにoutputText
コンポーネントのテキストをページに表示または非表示します。
また、必要なテキストをフィールドに入力する前にユーザーがラジオ・ボタンを選択できるようにするとします。送信アクションを自動的にトリガーするようにラジオ・ボタン・コンポーネントを設定し、そのimmediate
属性をtrue
に設定してinputText
コンポーネントより前に処理されるようにすることもできますが、valueChangeEvent
リスナーを追加し、その中でレスポンスのレンダリング・フェーズをコールして、ラジオ・ボタンが処理されるときに入力テキスト・コンポーネントに対して検証が実行されないようにする必要もあります。
このコードをリスナーに記述するかわりに、ADF Facesでは、ページに境界を設定してライフサイクルが境界内のコンポーネントでのみ実行されるようにすることができます。イベント・ルート・コンポーネントによってページでのこれらの境界が決まるため、ライフサイクルはその境界内のコンポーネントでのみ実行できます。イベント・ルート・コンポーネントは、2通りの方法で決定できます。
コンポーネント: 特定のコンポーネントが常にイベント・ルート・コンポーネントになります。領域とポップアップは、フレームワークが境界として認識しているコンポーネントの例です。リージョンまたはポップアップの内部でトリガーされるイベントに関係なく、ライフサイクルはリージョンまたはポップアップの外部のコンポーネントでは実行されません。
イベント: 特定のイベントはルートであるコンポーネントを示します。たとえば、showDetail
コンポーネントを展開または縮小する(9.8項「コンテンツの動的な表示および非表示」を参照)と送信される表示イベントは、showDetail
コンポーネントがルートであることを示すため、ライフサイクルはshowDetail
コンポーネントと任意の子コンポーネントでのみ実行されます。
イベント・ルートで実行されているライフサイクルおよびその子コンポーネントとは別に、ライフサイクルはそのイベントをリスニングするように構成された任意のコンポーネントでも実行できます。イベントを処理するためにルート・コンポーネントでイベントをリスニングするコンポーネントの構成は、クロスコンポーネント・リフレッシュと呼ばれます。
クロスコンポーネント・リフレッシュでは、あるコンポーネントがターゲットと呼ばれる別のコンポーネントのトリガーとして機能するように依存関係を設定できます。トリガー・コンポーネントでイベントが発生すると、ターゲット・コンポーネントと、トリガーおよびターゲットの両方の子コンポーネントでライフサイクルが実行され、これらのコンポーネントのみがレンダリングされます。これは、部分ページ・レンダリング(PPR)とみなされます。
ラジオ・ボタンの例で、ラジオ・ボタンおよび出力テキスト・コンポーネントでのみライフサイクルを実行するには、例5-3に示すように、ラジオ・ボタンをトリガーとし、出力テキストを含むpanelGroupLayout
コンポーネントをターゲットとして設定します。
ヒント: 出力テキストは、非表示として構成されている場合にレンダリングされないため、ターゲットにすることができません。したがって、panelGroupLayout コンポーネントに配置され、これがターゲットとして構成されます。 |
例5-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の使用方法の詳細は、第8章「部分ページ・コンテンツの再レンダリング」を参照してください。
PPRで特定のコンポーネントの検証を回避できない場合があります。たとえば、例5-4に示すように、panelGroupLayout
コンポーネント内にoutputText
コンポーネントのかわりにinputText
コンポーネントを使用し、このrequired
属性をtrue
に設定するとします。
例5-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>
この例では、ライフサイクルはルート(selectBooleanRadio
コンポーネント)、ターゲット(panelGroupLayout
コンポーネント)およびターゲットの子(inputText
コンポーネント)で実行されるため、inputText
コンポーネントは検証されます。inputText
コンポーネントが必須としてマークされているため、値がないと、検証が失敗し、エラーがスローされます。エラーのため、ライフサイクルはレスポンスのレンダリング・フェーズに移り、モデルは更新されません。このため、ラジオ・ボタンの値が更新されず、panelGroupLayout
コンポーネントは表示または非表示にできません。
このような場合、ラジオ・ボタンのimmediate
属性を使用して検証を省略できます。これにより、inputText
コンポーネントの検証の処理フェーズの前にボタンのvalueChangeEvent
が実行されます。レスポンスのレンダリング・フェーズをコール(その結果、入力コンポーネントの検証を省略)するvalueChangeListener
ハンドラ・メソッドを追加し、ラジオ・ボタンと入力コンポーネントに値を設定する必要があります。例5-5に、これを行うJSFコードを示します。
例5-5 immediate属性とvalueChangeListenerの使用
<af:form> <af:selectBooleanRadio immediate="true" valueChangeListener="#{validate.toggle}" 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>
例5-6に、valueChangeListener
のコードを示します。
inputListOfValues
およびinputComboBoxListOfValues
コンポーネントの場合、5.3.1項「immediate属性と最適化されたライフサイクルの使用について」で説明されている手順は機能しません。次に例を示します。
図5-7に示すように、ユーザーが従業員名を選択するinputListOfValues
コンポーネントと、必須属性がtrue
に設定され、従業員の選択後にその従業員のID番号で更新されるinputText
コンポーネントがあるとします。
これを実現するには、例5-7に示すように、Enameフィールドを部分トリガーとして持つようにEmpnoフィールドを設定できます。
例5-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"/>
5.3.1項「immediate属性と最適化されたライフサイクルの使用について」のラジオ・ボタンおよび入力コンポーネントの例と同様に、ユーザーが検索アイコンをクリックすると、ルート(inputListOfValues
コンポーネント)とターゲット(inputText
コンポーネント)の両方でライフサイクルが実行されるため、inputText
コンポーネントが検証されます。図5-8に示すように、inputText
コンポーネントが必須としてマークされているため、値がないと、検証が失敗し、エラーがスローされます。
ただし、LOVコンポーネントのimmediate属性をtrue
に設定し、LOVでValueChange
Listenerを使用するという5.3.1項「immediate属性と最適化されたライフサイクルの使用について」で推奨されているソリューションは、検証エラーを修正しません。LOVコンポーネントの場合、ValueChangeEvent
はLOVコンポーネントの値が変更されたときにのみキューに入れられます。そのため、immediate
属性をtrue
に設定しても、ユーザーが検索アイコンをクリックしたときに効果はありません。その時点でADF LaunchPopupEvent
はimmediate
属性の値にかかわらず常にアプリケーションの呼出しフェーズのキューに入れられるからです。つまり、最適化されたライフサイクルは、ルート・コンポーネントとターゲット・コンポーネントの両方で通常どおり実行されるため、入力コンポーネントは検証エラーをスローします。
ユーザーが「値リスト」ポップアップから新しい値を選択すると、LOVコンポーネントは2つのイベントをキューに入れます。1つは、コンポーネントの値の変化を示すValueChangeEvent
です。2つ目は、アプリケーションの呼出しフェーズについてキューに入れられるReturnPopupEvent
です。これはアプリケーション・メソッドに選択を処理する機会を与えます。LOVが期待どおりに動作するためには、この両方のイベントが発生する必要があります。
前述のように、LOVコンポーネントはユーザーが新しい値を選択したときのにみValueChangeEvent
をキューに入れます。LOVコンポーネントでimmediate
属性をtrue
に設定した場合、このイベントはリクエスト値の適用フェーズのキューに入れられ、新しい値が検証されます。また、図5-9に示すように、LOVコンポーネントのValueChangeListener
メソッドを作成し、その実装でレスポンスのレンダリング・フェーズに移動して入力コンポーネントの検証を回避した場合、選択した値はモデルにプッシュされず、ReturnPopupListener
はアプリケーションの呼出しフェーズ中に決してコールされず、ターゲット入力コンポーネントは新しい値で更新されません。
ValueChangeEvent
とReturnPopupEvent
の両方を同じリクエストの一部としてキューに入れる必要性と、新規に選択された値でターゲット・フィールドをリフレッシュする必要性の問題を解決するには、LOVコンポーネントを入力コンポーネントの部分トリガーとして宣言的に設定し、ValueChangeListener
のメソッドを作成するかわりに、ReturnPopupEvent
のリスナーを作成する必要があります。このリスナーは、入力コンポーネントをLOVの部分ターゲットとしてプログラムで設定する必要があります。ReturnPopupListener
メソッドが実行されるまで入力コンポーネントはLOVのターゲットでなくなり、ライフサイクルが実行されないため検証は失敗しないため、LOVのimmediate
属性をtrue
に設定する必要はありません。また、リスナー・メソッドはValueChangeEvent
ではなくReturnPopupEvent
に使用されるため、両方のイベントをキューに入れ、それに応じてモデルを更新できます。
例5-8に、LOVと入力コンポーネントに必要なページ・コードを示します。
例5-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にインスタンスを格納し、リスナー・メソッドがインスタンスにアクセスできるようにします。例5-9に示すように、リスナー・メソッドは、入力コンポーネントにアクセスし、それをLOVの部分ターゲットとして設定します。
部分ページ・レンダリングのプログラムによる設定の詳細は、8.3項「部分ページ・レンダリングのプログラムによる有効化」を参照してください。
ADF Facesフレームワークは、クライアント側変換および検証を提供します。サーバーにアクセスせずにページで実行できる独自のJavaScriptベースのコンバータおよびバリデータを作成できます。
クライアント側検証を使用して、特定のクライアント・イベントがキューに入れられたときに、適切なフォームまたはサブフォームのクライアント検証をトリガーできます(サブフォームの詳細は、5.5項「サブフォームを使用したページでのセクションの作成」を参照してください)。このクライアント検証が失敗し、既知のエラーがあることがわかった場合は、通常サーバーに伝播するイベント(たとえば、フォームが送信されたときのコマンド・ボタンのactionEvent
)はサーバーに移動しません。イベントを配信しないことは、何も送信されず、クライアント・リスナーがコールされないことも意味します。これは、サーバーで検証が失敗した場合にライフサイクルがレスポンスのレンダリング・フェーズにジャンプするサーバー側検証に似ています。アクション・イベントはキューに入れられますが、決して配信されず、actionListener
ハンドラ・メソッドは決してコールされません。
たとえば、ADF Facesで入力コンポーネントにrequired
属性を用意し、この検証がクライアントで実行されるとします。この属性をtrue
に設定すると、コンポーネントの値がnull
の場合、フレームワークでページにエラーが表示され、サーバーへの送信は必要ありません。例5-10に、inputText
コンポーネントのrequired
属性をtrue
に設定し、actionListener
属性がマネージドBeanのメソッドにバインドされているコマンド・ボタンを含むコードを示します。
例5-10 簡単なクライアント側検証の例
<af:form> <af:inputText id="input1" required="true" value="a"/> <af:commandButton text="Search" actionListener="#{demoForm.search}"/> </af:form>
このページを実行した場合、inputText
コンポーネントの値のフィールドをクリアし、フィールドからタブ移動すると、フィールドが赤い縁取りで再表示されます。フィールドをクリックすると、図5-10に示すように、値が必要なことを示すエラー・メッセージが表示されます。サーバーへの送信はなく、エラーの検出とメッセージの生成はすべてクライアントで行われます。
この例で、値のフィールドをクリアし、「検索」ボタンをクリックした場合、必須フィールドが空でありエラーが発生するため、ページは送信されません。アクション・イベントは配信されず、アクション・リスナーにバインドされたメソッドは実行されません。サーバーで検証が失敗したことをクライアントが通知できる場合はページを送信する理由がないため、このプロセスは適切です。
クライアント側の検証と変換の詳細は、第7章「入力の検証および変換」を参照してください。
JSF参照実装では、ページのセクションを別個に送信する場合、複数のフォームを使用する必要があります。ただし、複数のフォームにはページの状態の複数のコピーが必要なため、送信されないフォームでのユーザーの編集内容が失われる可能性があります。
ADF Facesには、別個に送信可能なページのセクションを示すサブフォーム・コンポーネントのサポートが追加されています。サブフォームのコンテンツは、サブフォーム内部のコンポーネントがページの送信に関与する場合にのみ検証(または処理)されるため、まったく別のフォーム要素を使用するという妥協をすることなく、検証されてモデルへプッシュされるコンポーネントのセットを比較的きめ細かく制御できます。サブフォームを使用するページの送信時に、ページの状態が一度だけ書き込まれ、すべてのユーザー編集内容が保持されます。
ベスト・プラクティス: 常にページごとに1つのみのform タグを使用します。複数のform タグを使用する必要がある場合は、subform タグを使用します。 |
サブフォームでは、ページがそのサブフォーム外のコンポーネントによって送信された場合でも、リクエスト値の適用フェーズでその子コンポーネントを常に実行できます。ただし、検証の処理フェーズとモデル値の更新フェーズはスキップされます(これは、送信されない場合はリクエスト値の適用フェーズを実行できない通常のフォーム・コンポーネントとは異なります)。サブフォーム外のコンポーネントが送信アクションの原因となった場合にサブフォーム内のコンポーネントを検証の処理フェーズとモデル値の更新フェーズで処理できるようにするには、default
属性を使用します。サブフォームのdefault
属性をtrue
に設定すると、そのサブフォームはほとんどの点で他のサブフォームと同様に動作しますが、ページのどのサブフォームにもその子コンポーネントからの適切なイベントがない場合、default
がtrue
に設定されたサブフォームは、その子コンポーネントの1つが送信の原因となったかのように動作します。サブフォームの詳細は、11.2項「formの定義」を参照してください。
実行時、ページがデータにアクセスできるオブジェクト・スコープに必要なデータを格納することで、データをページに渡します。スコープによってオブジェクトの存続期間が決まります。オブジェクトをスコープに置くと、EL式を使用してスコープからアクセスできます。たとえば、foo
という名前のマネージドBeanを作成し、Beanをリクエスト・スコープに存続させるよう定義するとします。このBeanにアクセスするには、#{requestScope.foo}
という式を使用します。
標準JSFアプリケーションには、次の5種類のスコープがあります。
application
Scope
: オブジェクトは、アプリケーションの間使用できます。
session
Scope
: オブジェクトは、セッションの間使用できます。
viewScope
: オブジェクトは、ユーザーが現在のビューとの対話を終了するまで使用可能です。オブジェクトはUIViewRoot
オブジェクトのマップに格納されます。このオブジェクトは、ページがリフレッシュされるかビューにリダイレクトされると空になります。
ヒント: ページのリフレッシュまたは同じビューへのリダイレクト後もオブジェクトを残す必要がある場合は、ADF FacesバージョンのviewScope を使用します。 |
flashScope: オブジェクトは、単一のビュー遷移中に使用可能で、次のビューに移動する前にクリアされます。flashScope
にパラメータ値を配置し、結果のページで使用可能にし、リダイレクト後も残すことができます。
request
Scope
: オブジェクトは、HTTPリクエストが送信されてから、レスポンスがクライアントに返るまでの間使用できます。
標準JSFスコープに加え、ADF Facesには次のスコープが用意されています。
pageFlow
Scope
: オブジェクトは、ユーザーがあるページから別のページへのナビゲートを続けるかぎり使用できます。ユーザーが新たなブラウザ・ウィンドウを開いてナビゲートを始めた場合、そのウィンドウで別のpageFlowScope
スコープを持ちます。
backingBean
Scope
: ページ・フラグメントと宣言コンポーネントのみに対するマネージドBeanに使用します。オブジェクトは、HTTPリクエストが送信されてから、レスポンスがクライアントに返るまでの間使用できます。このスコープは、複数のページ・フラグメントと宣言コンポーネントがページにある場合があり、値の競合を避けるために別々のスコープ・インスタンスに値を保持するために必要です。ページ・フラグメントまたは宣言コンポーネントに対して作成されるマネージドBeanには、backingBeanScope
スコープを使用します。
view
Scope
: オブジェクトは、現在のビューのIDが変更されるまで使用できます。viewScope
スコープを使用して特定のページの値を保持します。JSF viewScope
とは異なり、ADF FacesのviewScope
に格納されるオブジェクトは、ページのリフレッシュや同じビューIDへのリダイレクト後も残ります。
注意: これらは標準JSFスコープではないため、EL式には、Beanを参照するためのスコープを明示的に含める必要があります。たとえば、pageFlowScope スコープからMyBean マネージドBeanを参照するには、#{pageFlowScope.MyBean} という式になります。 |
オブジェクト・スコープは、プログラミング言語のグローバル変数およびローカル変数のスコープに相当します。スコープが広いほど、オブジェクトの可用性が高くなります。存続期間の間、これらのオブジェクトで特定のインタフェースの公開、情報の保持および他のオブジェクトへの変数とパラメータの受渡しを行うことができます。たとえば、sessionScope
スコープに定義されているマネージドBeanは、複数のページ・リクエストの間使用できます。しかし、requestScope
スコープに定義されているマネージドBeanは、1つのページ・リクエストの間のみ使用できます。
図5-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
スコープとの唯一の違いは、オブジェクト名にpageFlow
Scope
接頭辞を使用する必要があることです。たとえば、ボタンのラベルを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コード内からアクセスできます。終了したら、スコープを必ずクリアしてください。
注意: アプリケーションでOracle ADF Controllerが使用されている場合は、スコープを手動でクリアする必要はありません。 |
前提作業
オブジェクト・スコープに関する知識が役立つ場合があります。詳細は、5.6項「オブジェクト・スコープ・ライフサイクル」を参照してください。pageFlowスコープを使用して値を渡す方法も理解しておく必要があります。詳細は、5.7項「ページ間の値の受渡し」を参照してください。
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で使用されていた可能性がある)setActionListener タグを使用するかわりに、setPropertyListener タグを使用し、イベント・タイプをaction に設定します。 |
pageFlowScopeスコープに値を設定する手順:
値の設定元のページで、コンポーネント・パレットを使用してコマンド・コンポーネントを作成します。
コンポーネント・パレットで、「操作」パネルの「リスナー」グループから、「プロパティ・リスナーの設定」をドラッグしてコマンド・コンポーネントに子としてドロップします。
または、コンポーネントを右クリックし、「ボタンの中に挿入」→「ADF Faces」→「setPropertyListener」の順に選択します。
「プロパティ・リスナーの設定の挿入」ダイアログで、「自」フィールドに別のコンポーネントに設定される値を設定します。
たとえば、従業員の名前を格納するMyBean
という名前のマネージドBeanがあり、次のページに値を受け渡すとします。「自」フィールドに#{myBean.empName}
と入力します。
「至」フィールドにpageFlowScope
スコープでの値を設定します。
たとえば、「至」フィールドに#{pageFlowScope.empName}
と入力します。
「タイプ」ドロップダウン・メニューから、「アクション」を選択します。
これによって、リスナーで、コマンド・コンポーネントに関連付けられているアクション・イベントをリスニングできます。
pageFlowScopeスコープから値にアクセスする手順:
値のアクセス元のページに、値を表示するコンポーネントをドロップします。
コンポーネントの値を、setPropertyListener
タグで設定したTo
値と同じ値に設定します。
たとえば、outputText
コンポーネントで従業員名にアクセスするには、このコンポーネントの値を#{pageFlowScope.empName}
に設定します。