Oracle® Fusion Middleware Oracle Application Development Framework Webユーザー・インタフェース開発者ガイド 11gリリース1 (11.1.1.7.0) B52029-07 |
|
前 |
次 |
この章では、サーバーおよびクライアントでのイベントの処理方法について説明します。
この章では、次の項目について説明します。
従来のJSFアプリケーションでは、イベント処理は通常サーバーで行われます。JSFのイベント処理はJavaBeansイベント・モデルをベースにしており、JSFアプリケーションでイベント・クラスおよびイベント・リスナー・インタフェースを使用して、コンポーネントで生成されたイベントが処理されます。
アプリケーションでのユーザー・イベントの例には、ボタンまたはリンクのクリック、メニューまたはリストからの項目の選択、入力フィールドの値の変更などがあります。ボタンのクリックなどのユーザー・アクティビティが発生すると、イベントの情報を格納し、イベントを生成したコンポーネントを特定するイベント・オブジェクトがコンポーネントで作成されます。イベントはイベント・キューにも追加されます。JSFライフサイクルの適切なタイミングで、登録された適切なリスナーにイベントをブロードキャストするようにJSFからコンポーネントに指示し、リスナーでは、イベントを処理するリスナー・メソッドが起動されます。リスナー・メソッドでは、ユーザー・インタフェースでの変更のトリガーまたはバックエンド・アプリケーション・コードの起動、あるいはその両方が実行される場合があります。
標準JSFコンポーネントと同様に、コンポーネントがアクティブになると、ADF FacesコンポーネントでActionEvent
イベントが配信され、コンポーネントのローカル値が変更されると、ADF Facesの入力および選択コンポーネントでValueChangeEvent
イベントが配信されます。
たとえば、File Explorerアプリケーションでは、commandMenuItem
コンポーネントを使用してユーザーがファイルまたはフォルダを新たに作成できるサブメニューが「ファイル」メニューに含まれます。ユーザーが「フォルダ」commandMenuItem
をクリックすると、ActionEvent
イベントが起動されます。コンポーネントのactionListener
属性の値として設定されているEL式がheaderManager
マネージドBeanのcreateNewDirectory
メソッドと解決されるため、このメソッドが起動されてディレクトリが新たに作成されます。
注意: 組込みイベント機能を持つADF Facesコンポーネントは |
ADF Facesは標準JSFイベント処理手法に準拠していますが、次の2つの主要な方法を提供することでイベント処理機能を向上させています。
AJAXベースの機能(部分ページ・レンダリング)
クライアント側イベント・モデル
標準JSFイベントとは異なり、ADF FacesイベントではAJAXスタイルの部分ポストバックがサポートされ、部分ページ・レンダリング(PPR)が行えます。フル・ページ・レンダリングのかわりに、ADF Facesのイベントおよびコンポーネントでは、リクエスト時にページの一部のみがリフレッシュされる部分ページ・レンダリングがトリガーされます。
特定のコンポーネントがイベント・ルート・コンポーネントとみなされます。イベント・ルート・コンポーネントによってページでの境界が決まり、その境界内のコンポーネントにのみライフサイクルが実行されます(ライフサイクルのこの機能の詳細は、4.3項「最適化されたライフサイクルの使用」を参照)。イベント・ルート内でイベントが発生すると、ルートの子であるこれらのコンポーネントのみがページでリフレッシュされます。イベント・ルート・コンポーネントの例はポップアップです。ポップアップ内でイベントが発生すると、ポップアップとその子のみが再レンダリングされ、ページ全体は再レンダリングされません。
イベント・ルート・コンポーネントとみなされるコンポーネントは、次のとおりです。
popup
region
panelCollection
calendar
editableValueHolder
コンポーネント(inputText
など)
また、特定のイベントは、特定のコンポーネントがイベント・ルート・コンポーネントであることを示します。たとえば、showDetail
コンポーネントを展開または縮小する(8.9項「コンテンツの動的な表示および非表示」を参照)と送信される表示イベントは、showDetail
コンポーネントがルートであることを示します。ライフサイクルはshowDetail
コンポーネント(および子コンポーネントまたはこれをトリガーとするその他のコンポーネント)でのみ実行され、展開または縮小時にのみが再レンダリングされます。
表5-1に、ADF Facesでのイベント・タイプと、ソース・コンポーネントがイベント・ルートかどうかを示します。
表5-1 イベントとイベントのルート・コンポーネント
イベント・タイプ | コンポーネントのトリガー先 | イベント・ルートか |
---|---|---|
|
すべてのコマンド・コンポーネント |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
すべてのコマンド・コンポーネント |
NA |
|
|
|
|
|
NA |
|
|
|
|
|
NA |
|
|
NA |
|
|
NA |
|
すべてのコンポーネント |
NA |
|
|
|
|
|
|
|
|
NA |
|
|
NA |
|
すべてのコマンド・コンポーネント |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
すべての入力および選択コンポーネント( |
|
ヒント: イベント・ルートを処理したときにイベント・ルートの外部にあるコンポーネントを処理する必要がある場合は、それらのコンポーネントの |
サーバー側のアクションおよび値変更イベントの他に、ADF Facesコンポーネントでは、クライアント側のアクションおよび値変更イベント、その他のサーバーおよびクライアント・イベントが起動されます。サーバー・コンポーネントとクライアント・コンポーネントの両方で生成されるイベント(選択イベントなど)も、サーバー・コンポーネントでのみ生成されるイベント(起動イベントなど)も、クライアント・コンポーネントでのみ生成されるイベント(ロード・イベントなど)もあります。
デフォルトでは、ほとんどのクライアント・イベントはサーバーに伝播されます。コンポーネントの状態の変更は、状態に矛盾がないよう自動的にサーバーと同期され、必要に応じてイベントがサーバーに送信され、追加処理が実行されます。ただし、イベントが伝播されないように構成できます。
また、サーバー側Javaコンポーネントにクライアント側イベント・リスナーを登録すると、RCFでJavaScriptコンポーネントが必要であるとみなされ、クライアント側コンポーネントが作成されます。
クライアント側JavaScriptイベントのソースはいくつかあります。DOMイベントやプロパティ変更イベントから自動的に導出されることもあれば、他のイベントの処理時に手動で作成することもできます。
ADF Facesには、多数のサーバー側イベントが用意されています。表5-2に、サーバーのADF Facesコンポーネントで生成されるイベントと、これをトリガーするコンポーネントを示します。
表5-2 ADF Facesサーバー・イベント
イベント | トリガー元コンポーネント |
---|---|
|
すべてのコマンド・コンポーネント |
|
|
|
|
|
|
|
すべてのコマンド・コンポーネント |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
すべてのコマンド・コンポーネント |
|
|
|
|
|
|
|
|
|
すべての入力および選択コンポーネント( |
*このフォーカスイベントは、特定のサブツリーがフォーカスされると生成され、クライアント側のキーボード・フォーカス・イベントとは異なります。
**LoadEvent
イベントは、初期ページの表示後に起動されます(データ・ストリーミング結果が遅れて到着する場合があります)。
すべてのサーバー・イベントには、関連付けられたコンポーネントに対するイベント・リスナーがあります。そのイベントを処理するハンドラを作成し、そのハンドラ・コードをコンポーネントのリスナーと関連付ける必要があります。
たとえば、File Explorerアプリケーションの場合、ユーザーが表の行を選択すると選択イベントが起動されます。表のselectionListener
属性は、TableContentView.java
マネージドBeanのtableSelectFileItem
ハンドラ・メソッドにバインドされているため、このメソッドがイベントに対応して起動されます。
サーバー側イベントを処理する手順:
マネージドBean(イベント・リスナーを使用するページの場合はバッキングBean)に、イベント(イベント・タイプ)を唯一のパラメータとし、void
を返すpublicメソッドを作成します。例5-1に、tableSelectFileItem
ハンドラのコードを示します。(マネージドBeanの作成と使用の詳細は、2.6項「マネージドBeanの作成と使用」を参照してください。)
例5-1 イベント・リスナー・メソッド
public void tableSelectFileItem(SelectionEvent selectionEvent) { FileItem data = (FileItem)this.getContentTable().getSelectedRowData(); setSelectedFileItem(data); }
ヒント: イベント・リスナー・コードがアプリケーションの複数ページで使用されることが多い場合、すべてのページでアクセスできるイベント・リスナー実装クラスの作成を検討します。すべてのサーバー・イベント・リスナー・クラスの実装では、 たとえば、 public void processLaunch (LaunchEvent evt) { // your code here } |
イベント・リスナー・メソッドをコンポーネントに登録するには、構造ウィンドウでイベントを起動するコンポーネントを選択します。プロパティ・インスペクタでイベント・リスナー・プロパティの隣にあるドロップダウン・メニューを使用し、「編集」を選択します。
「属性の編集」ダイアログで、ステップ1で作成したマネージドBeanとメソッドを選択します。
例5-2に、選択イベント・リスナー・メソッドをtable
コンポーネントに登録するサンプル・コードを示します。
大部分のコンポーネントはクライアント側イベントでも使用できます。クライアントでイベントを処理すると、サーバーへのラウンドトリップが不要になります。クライアント側イベントを使用する場合、マネージドBeanにイベント・ハンドラ・コードを含めるかわりに、JavaScriptを使用し、コール元ページにもJavaScriptライブラリにも含めることができます。
デフォルトでは、クライアント・イベントはクライアントでのみ処理されます。ただし、ボタンがクリックされたことを示すAdfActionEvent
イベントのようにサーバーに配信されるイベント・タイプもあります。コンポーネントの状態に応じてサーバーに配信されるイベントもあります。たとえば、AdfValueChangeEvent
イベントは、autoSubmit
属性がtrue
に設定されている場合サーバーに配信されます。追加処理が不要な場合は、イベントのサーバーへの配信を取り消すことができます。ただし、取り消すことができないクライアント・イベントもあります。たとえば、popupOpened
イベント・タイプは、ポップアップ・ウィンドウが開くと配信されるため、このイベントのサーバーへの配信は取り消すことができません。
パフォーマンスのヒント: イベントのサーバーでの処理が不要な場合、イベントがサーバーに伝播されないよう、処理の最後にイベントを取り消すことを検討してください。詳細は、5.3.5項「イベントがサーバーに伝播されないようにする方法」を参照してください。 |
表5-3に、ADF Facesクライアント・コンポーネントで生成されるイベント、イベントがサーバーに送信されるかどうか、イベントが取消し可能かどうか、およびイベントをトリガーするコンポーネントをリストします。
表5-3ADF Facesクライアント・イベント
イベント・タイプ | イベント・クラス | サーバーへの伝播 | 取消し可能 | トリガー元コンポーネント |
---|---|---|---|---|
|
|
◎ |
◎ |
すべてのコマンド・コンポーネント |
|
|
◎ |
◎ |
ユーザーがダイアログでOKボタンまたは取消しボタンを選択したとき |
|
|
◎ |
◎ |
公開状態がユーザーによって切り替えられたとき |
|
◎ |
◎ |
|
|
|
◎ |
◎ |
|
|
|
|
◎ |
◎ |
内部の |
|
|
◎ |
◎ |
ドキュメントのコンテンツがクライアントで表示された後(PPRナビゲーションが使用されている場合も含む)。onLoad DOMイベントには常に対応しません。 |
|
◎ |
◎ |
|
|
|
|
× |
× |
ポップアップ・ウィンドウまたはダイアログが開いた後 |
|
|
× |
◎ |
ポップアップ・ウィンドウまたはダイアログが開く前 |
|
|
× |
× |
ポップアップ・ウィンドウまたはダイアログが閉じた後 |
|
|
× |
× |
すべてのコンポーネント |
|
|
◎ |
◎ |
問合せアクション時(ユーザーが検索アイコンまたは検索ボタンをクリックしたとき) |
|
|
◎ |
◎ |
|
|
◎ |
◎ |
すべてのコマンド・コンポーネント |
|
|
◎ |
◎ |
|
|
|
◎ |
◎ |
|
|
|
|
◎ |
◎ |
行の公開状態が切り替えられたとき |
|
|
◎ |
◎ |
選択状態が変わったとき |
|
|
◎ |
◎ |
ユーザーが表のデータをソートしたとき |
|
|
× |
◎ |
すべて |
|
|
◎ |
◎ |
すべての入力および選択コンポーネント( 入力または選択コンポーネントの値が変更されたとき |
ADF Facesでは、表5-4に示すように、クライアント・キーボードおよびマウス・イベントもサポートします。
表5-4 サポートされるキーボードとマウスのイベント・タイプ
イベント・タイプ | イベントが発生するタイミング |
---|---|
|
ユーザーがコンポーネントをクリックしたとき |
|
ユーザーがコンポーネントをダブルクリックしたとき |
|
ユーザーがコンポーネント上でマウス・ボタンを押したとき |
|
ユーザーがコンポーネント上でマウス・ボタンを離したとき |
|
ユーザーがコンポーネント上でマウスを移動したとき |
|
コンポーネント内にマウスが移動したとき |
|
コンポーネントからマウスが移動したとき |
|
コンポーネントのフォーカス中にユーザーがキーを押したとき |
|
コンポーネントのフォーカス中にユーザーがキーを離したとき |
|
コンポーネントのフォーカス中にキーを押す動作が正常に完了したとき |
|
コンポーネントがキーボード・フォーカスを得たとき |
|
コンポーネントがキーボード・フォーカスを失ったとき |
ベスト・プラクティス: キーボードとマウスのイベントは、 |
clientListener
タグによって、クライアント側イベント・ハンドラ・スクリプトをコンポーネントに登録する宣言的方法が提供されます。サポートされているクライアント・イベント・タイプが発生すると、スクリプトが起動されます。例5-3に、アクション・イベントに関連付けられているJavaScript関数の例を示します。
例5-3 clientListenerタグ
<af:commandButton id="button0" text="Do something in response to an action"> <af:clientListener method="someJSMethod" type="action"/> </af:commandButton>
ヒント: コンポーネントのJavaScriptプロパティのかわりに |
クライアント側イベントを使用するには、まず、イベントを処理するJavaScriptを作成する必要があります。次に、clientListener
タグを使用します。
クライアント側イベントを使用する手順:
JavaScriptイベント・ハンドラ関数を作成します。JavaScriptの作成の詳細は、3.3項「JavaScriptのページへの追加」を参照してください。この機能内で追加できるのは、次のとおりです。
ページでのクライアント・コンポーネントの検索
イベント・ハンドラを別のコンポーネントで処理する場合、そのコンポーネントをページで検索する必要があります。たとえば、File Explorerアプリケーションでユーザーが「ヘルプ」メニューのフィードバック・メニュー項目を選択した場合、関連付けられているJavaScript関数でヘルプ・ポップアップ・ダイアログを開くには、このダイアログを検索する必要があります。クライアント・コンポーネントの検索の詳細は、3.5項「ページでのクライアント・コンポーネントの検索」を参照してください。
イベントの発生元の戻し
ページに複数の同じコンポーネントがある場合、JavaScript関数でイベントを発行したコンポーネントを判断する必要があります。たとえば、同じポップアップ・ダイアログを開くことができる複数のコンポーネントがあり、コール元のコンポーネントと並べてそのダイアログを表示するとします。ポップアップ・ダイアログを並べる場所を決めるには、AdfLaunchPopupEvent
のソースが認識できる必要があります。詳細は、5.3.2項「イベントの発生元を返す方法」を参照してください。
クライアント属性の追加
クライアント・イベント・ハンドラでコンポーネントの特定の属性を使用する必要が生じる場合があります。たとえば、File Explorerアプリケーションでユーザーが「ヘルプ」メニューの「バージョン情報」メニュー項目を選択すると、ユーザーがフィードバックを提供できるダイアログが起動します。このダイアログを開いて表示する関数は他のダイアログでも使用され、別個に表示する必要があります。このため、表示するダイアログとダイアログの配置情報を関数で認識できる必要があります。この情報はクライアント属性に保持されます。クライアント属性は、カスタムのサーバー側属性のクライアントへのマーシャリングにも使用できます。詳細は、5.3.3項「イベントのクライアント側属性の使用方法」を参照してください。
サーバーへの伝播の取消し
表5-3に示すように、一部のコンポーネントではクライアント側イベントがサーバーへ伝播されます。この追加処理が不要な場合、伝播を取り消すことができます。詳細は、5.3.5項「イベントがサーバーに伝播されないようにする方法」を参照してください。
JavaScript関数の作成後、イベント・メソッドをコールするイベント・リスナーを追加する必要があります。
注意: また、ADF Facesコンポーネントのすべてのクライアント・イベントもクライアント動作として公開されるため、JSF 2.0クライアント動作タグ( |
JavaScriptを起動するコンポーネントを選択し、プロパティ・インスペクタでClientComponentをtrueに設定します。
コンポーネント・パレットの「操作」パネルから、「クライアント・リスナー」を、選択したコンポーネントの子としてドラッグ・アンド・ドロップします。
「クライアント・リスナーの挿入」ダイアログでメソッドを入力し、JavaScript関数のタイプを選択します。
clientListener
タグのmethod
属性で、対応するイベントが発生したときにコールするJavaScript関数を指定します。JavaScript関数は1つのパラメータ(イベント・オブジェクト)をとる必要があります。
clientListener
タグのtype
属性で、action
、valueChange
などのタグでリスニングするクライアント・イベント・タイプを指定します。表5-3に、ADF Facesクライアント・イベントを示します。
clientListener
タグのtype
属性では、キーボードおよびマウスのイベントに関連するクライアント・イベント・タイプもサポートされます。表5-4に、キーボードとマウスのイベント・タイプを示します。
例5-4に、Explorer.js
JavaScriptファイルのshowHelpFileExplorerPopup
関数の起動に使用されるコードを示します。
コンポーネント・パレットの「操作」パネルからクライアント属性をドラッグして選択したコンポーネントの子としてドロップし、必要な属性を追加します。プロパティ・インスペクタで属性の名前と値を入力します。例5-5に、showAboutFileExplorerPopup
関数の属性値の設定に使用されるコードを示します。
例5-5 属性の追加
<af:commandMenuItem id="aboutMenuItem" text="#{explorerBundle['menuitem.about']}" clientComponent="true"> <af:clientListener method="Explorer.showAboutFileExplorerPopup" type="action"/> <af:clientAttribute name="popupCompId" value=":fe:aboutPopup"/> <af:clientAttribute name="align" value="end_after"/> <af:clientAttribute name="alignId" value="aboutMenuItem"/> </af:commandMenuItem>
注意:
|
JavaScriptメソッドgetSource()
は、クライアント・イベントの発生元を返します。たとえば、File Explorerアプリケーションには、例5-6に示すshowAboutFileExplorerPopup
関数が含まれており、クライアント属性を使用して値が受け渡され、特定のポップアップ・ダイアログまたはウィンドウの位置合せの設定に複数のイベントで使用できます。関数を使用する各イベントで属性の値が異なる場合があるため、対応する属性値にアクセスできるようイベントの発生元を関数で認識する必要があります(クライアント属性の使用の詳細は、5.3.3項「イベントのクライアント側属性の使用方法」を参照してください)。
例5-6 クライアント・イベントのソース・コンポーネントの検出
Explorer.showAboutFileExplorerPopup = function(event)
{
var source = event.getSource();
var alignType = source.getProperty("align");
var alignCompId = source.getProperty("alignId");
var popupCompId = source.getProperty("popupCompId");
source.show({align:alignType, alignId:alignCompId});
event.cancel();
}
getSource()
メソッドがコールされ、現在のフォーカス・イベントを発生したクライアント・コンポーネント(このケースではポップアップ・コンポーネント)が決まります。
スクリプトのロジックでコンポーネントになんらかの変更を加える場合があります。これを行うには、イベントによって受け渡される属性値が必要な場合があります。たとえば、File Explorerアプリケーションには、例5-7に示すshowAboutFileExplorerPopup
関数が含まれており、クライアント属性を使用して値が受け渡され、特定のポップアップ・コンポーネントの位置合せの設定に使用できます。属性値には、ソース・コンポーネントでgetProperty
メソッドをコールしてアクセスします。
例5-7 JavaScriptからアクセスされる属性値
Explorer.showAboutFileExplorerPopup = function(event) { var source = event.getSource(); var alignType = source.getProperty("align"); var alignCompId = source.getProperty("alignId"); var popupCompId = source.getProperty("popupCompId"); var aboutPopup = event.getSource().findComponent(popupCompId); aboutPopup.show({align:alignType, alignId:alignCompId}); event.cancel(); }
例5-8に示すように、値はソース・コンポーネントで設定されます。
例5-8 コンポーネントでの属性の設定
<af:commandMenuItem id="aboutMenuItem" text="#{explorerBundle['menuitem.about']}" clientComponent="true"> <af:clientListener method="Explorer.showAboutFileExplorerPopup" type="action"/> <af:clientAttribute name="popupCompId" value=":aboutPopup"/> <af:clientAttribute name="align" value="end_after"/> <af:clientAttribute name="alignId" value="aboutMenuItem"/> </af:commandMenuItem>
このように属性を使用すると、同じイベントをトリガーする異なるコンポーネントでスクリプトを再利用できます。
実行時間が長いイベントの処理中に、ユーザーがUIを操作できないようにする場合があります。たとえば、アプリケーションがボタンを使用して命令を送信し、処理の一部にユーザーのアカウントに対する請求を作成する処理が含まれているとします。ユーザーが不注意でボタンを2回押した場合、そのアカウントは2回請求されることになります。サーバーの処理が完了するまでユーザーの操作をブロックすることで、誤ったクライアント・アクティビティが発生しないようにできます。
ADF Faces JavaScript APIには、AdfBaseEvent.preventUserInput
関数が含まれています。preventUserInput
関数をコールすると、ブラウザ・ウィンドウ全体がガラス・ペインで覆われ、イベントがサーバーへのラウンドトリップを完了するまで入力できなくなり、イベントの処理中にすべてのユーザー入力を阻止できます。
preventUserInput
関数は、カスタム・イベント、カスタム・クライアント・スクリプトで発生したイベント、またはカスタム・クライアント・コンポーネントのピアで発生したイベントでのみ使用できます。また、イベントをサーバーに伝播する必要があります。例5-9に、JavaScriptでpreventUserInput
を使用する方法を示します。
例5-9 UI入力のブロック
function queueEvent(event) { event.cancel(); // cancel action event var source = event.getSource(); var params = {}; var type = "customListener"; var immediate = true; var isPartial = true; var customEvent = new AdfCustomEvent(source, type, params, immediate); customEvent.preventUserInput(); customEvent.queue(isPartial); }
一部のクライアント・イベントは、クライアントでの処理が完了すると、デフォルトでサーバーに伝播されます。この伝播が行われないようにすることが必要な状況もあります。たとえば、ボタンがクリックされるとJavaScriptコードを実行するcommandButton
コンポーネントを使用している場合、サーバーにactionListener
イベント・リスナーがなければ、イベントを伝播することはリソースを無駄に消費することになります。サーバーへ伝播されないようにするには、リスナーでイベントのcancel()
関数をコールします。cancel()
関数がコールされると、isCanceled()
関数でtrue
が返されます。
例5-10に、伝播を取り消すshowAboutFileExplorerPopup
関数を示します。
例5-10 クライアント・イベントのサーバーへの伝播の取消し
Explorer.showAboutFileExplorerPopup = function(event)
{
var source = event.getSource();
var alignType = source.getProperty("align");
var alignCompId = source.getProperty("alignId");
var popupCompId = source.getProperty("popupCompId");
var aboutPopup = event.getSource().findComponent(popupCompId);
aboutPopup.show({align:alignType, alignId:alignCompId});
event.cancel();
}
イベントを取り消すことで、行われないデフォルト処理もあります。たとえば、ポップアップ・メニューのAdfUIInputEvent
イベントを取り消すと、このイベントを受けて表示されるポップアップ・メニューがブラウザで表示されません。
イベントが取り消せない場合、cancel()
関数コールは無視され、そのイベントはisCancelable()
関数でfalse
が返されることで示されます(取り消せないイベントは、表5-3で「取消し可能」列に「×」と表示されています)。これは、イベントが完了済の通知で、ブロックできないということを通常意味します。一度取り消したイベントの取消しを無効にすることもできません。
イベント処理は、一般にブラウザのネイティブ・イベント・ループから捕捉されます。ページでは、ドキュメントにバブル・アップされるすべてのDOMイベントを受け取り、そのDOMに関連付けられているピアに渡します。ピアでは、DOMイベントをラップするリッチ・クライアントJavaScriptイベント・オブジェクトが作成され、ページに返され、イベントがキューイングされます(ピアとADF Facesアーキテクチャの詳細は、第3章「ADF Facesアーキテクチャの使用」を参照してください)。
各DOMイベントのページでの処理(この結果、通常コンポーネント・イベントがキューイングされる)後、ページのイベント・キューは、通常ブラウザのイベント・ループの最後で空になります。ただし、タイマー起動時にポーリング・イベントを発生するポーリング・コンポーネントなど、ユーザー入力とは無関係にイベントがキューイングされることがあるため、イベントをキューイングすると、ユーザー入力がなくても、イベント・キューを空にするタイマーも起動されます。
イベント・キューは先入れ先出しキューです。イベント・キューを空にするには、ページで各イベント・オブジェクトを捕捉し、イベント・ソースのbroadcast()
関数に配信します。このループは、キューが空になるまで続きます。イベントのブロードキャストによって、導出された新規イベントのキューイングが間接的に引き起こされることは非常に理にかなっています(一般的です)。導出されたイベントは、同じループでブロードキャストされます。
イベントがコンポーネントにブロードキャストされると、コンポーネントで次の処理が行われます。
イベントをピアのDispatchComponentEvent
メソッドに配信します。
そのイベント・タイプ用に登録されているリスナーにイベントを配信します。
イベントをバブルする必要があるかどうかを確認し、必要な場合はバブルを開始します。ほとんどのイベントがバブルされます。例外には、プロパティ変更イベント(キューイングされず、このプロセスにまったく関与しない)やマウスの移動イベント(効率を高めるため)があります。
イベントがバブルされる時、イベントはAdfUIComponent
のHandleBubbledEvent
関数に配信され、ここからピアのDispatchComponentEvent
関数に渡されます。クライアント・イベント・リスナーではなく、ピアがイベントを受け取ることに注意してください。
イベントのバブルは、イベントのstopBubbling()
関数をコールしてブロックでき、その後、isBubblingStopped()
関数でtrue
が返され、バブルが止まります。取消し同様、このコールは元に戻すことはできません。
注意: イベントを取り消してもバブルは止まりません。イベントの取消しとバブルの停止の両方を行う場合は、両方の関数をコールする必要があります。 |
前述の処理でイベントが取り消されない場合は、AdfUIComponent.HandleEvent
メソッドをコールします。このメソッドでは、イベントでのリクエストに応じてイベントがサーバー・イベント・キューに追加されます。
ADF Facesのいくつかのコンポーネントは、pageTemplate
、subform
、table
およびtree
などのNamingContainer
コンポーネントです。NamingContainer
コンポーネントを含むページでクライアント側APIとイベントを使用する場合、ソース・コンポーネントでfindComponent()
メソッドを使用する必要があります。
たとえば、File Explorerアプリケーション内のページのコンポーネントはすべて最終的にはpageTemplate
コンポーネント内にあるため、例5-11に示すように、JavaScript関数でgetSource()
とfindComponent()
メソッドを使用する必要があります。getSource()
メソッドはAdfUIComponent
クラスにアクセスし、コンポーネントの検出に使用できます。
例5-11 findComponent()メソッドを使用するJavaScript
function showPopup(event) { event.cancel(); var source = event.getSource(); var popup = source.findComponent("popup"); popup.show({align:"after_end", alignId:"button"}); }
findComponent()
メソッドを使用すると、メソッドを起動したコンポーネントでローカルに検索が開始します。ネーミング・コンテナの使用の詳細は、3.5項「ページでのクライアント・コンポーネントの検索」を参照してください。
clientAttribute
タグでは、サーバーからクライアントへのボーナス属性の送信はサポートされますが、これらの属性のサーバーへの同期化は行われません。カスタム・データをサーバーに戻すには、AdfCustomEvent
クラスとserverListener
タグを使用して送信されるカスタム・イベントを使用します。
AdfCustomEvent.queue()
JavaScriptメソッドによって、clientComponent
属性がtrue
に設定されているコンポーネントからカスタム・イベントを起動できます。カスタム・イベント・オブジェクトには、クライアント・イベント・ソースと、イベントに含まれるパラメータのマップに関する情報が含まれます。カスタム・イベントは即時配信(リクエスト値の適用フェーズ中)にも非即時配信(アプリケーションの起動フェーズ中)にも設定できます。
たとえば、File Explorerアプリケーションでは、ユーザーは左側の検索フィールドにファイル名を入力した後、[Enter]キーを押して検索を起動します。例5-12 に示すように、これは、inputText
フィールドに、[Enter]キーが押されると、JavaScript関数を起動するclientListener
が含まれているためです。
例5-12 clientListenerによってJavaScript関数が起動され、ServerLIstenerが起動される
//Code on the JSF page... <af:inputText id="searchCriteriaName" value="#{explorer.navigatorManager.searchNavigator. searchCriteriaName}" shortDesc="#{explorerBundle['navigator.filenamesearch']}"> <af:serverListener type="enterPressedOnSearch" method="#{explorer.navigatorManager. searchNavigator.searchOnEnter}"/> <af:clientListener type="keyPress" method="Explorer.searchNameHandleKeyPress"/> </af:inputText> //Code in JavaScript file... Explorer.searchNameHandleKeyPress = function (event) { if (event.getKeyCode()==AdfKeyStroke.ENTER_KEY) { var source = event.getSource(); AdfCustomEvent.queue(source, "enterPressedOnSearch", {}, false); } }
JavaScriptには、イベント・ソース、カスタム・イベント・タイプとして文字列enterPressedOnSearch
、nullパラメータ・マップ、即時パラメータにFalse
をとるAdfCustomEvent.queue
メソッドが含まれています。
ページのinputText
にも、次のserverListener
タグが含まれています。
<af:serverListener type="enterPressedOnSearch"
method="#{explorer.navigatorManager.
searchNavigator.searchOnEnter}"/>
タイプ値enterPressedOnSearch
がJavaScriptのAdfCustomEvent.queue
メソッドのパラメータの値と同じであるため、メソッド式#{explorer.navigatorManager.searchNavigator.searchOnEnter}
に解決されるメソッドが起動されます。
クライアントからサーバーへカスタム・イベントを送信するには、カスタム・イベント・タイプを使用してクライアント・イベントを起動し、バッキングBeanにサーバー・リスナー・メソッドを記述し、このメソッドでカスタム・イベントを処理する必要があります。次に、サーバー・リスナーをコンポーネントに登録します。
カスタム・イベントを送信する手順:
イベント・ソース、カスタム・イベント・タイプおよびサーバーに送信するパラメータを指定してAdfCustomEvent.queue()
メソッドを使用し、カスタム・イベントを処理するJavaScriptを作成します。
たとえば、[Enter]キーが押されると検索機能を起動するJavaScriptでは、例5-13に示すようなイベント・ソース、カスタム・イベント・タイプとして文字列enterPressedOnSearch
、nullパラメータ・マップ、即時パラメータとしてFalse
をとるAdfCustomEvent.queue
メソッドを使用します。
マネージドBeanにサーバー・リスナー・メソッドを作成します。このメソッドはpublicで、oracle.adf.view.rich.render.ClientEvent
オブジェクトを引数とし、void
型を返します。例5-14に、別のメソッドをコールして検索を実行し、ナビゲータをリフレッシュするだけのSearchNavigatorView
マネージドBeanに使用されるコードを示します。
例5-14 カスタム・クライアント・イベント用サーバー・リスナー・メソッド
public void searchOnEnter(ClientEvent clientEvent) { doRealSearchForFileItem(); // refresh search navigator this.refresh(); }
注意: JavaからJavaScriptへの変換では、 |
コンポーネント・パレットの「操作」パネルからクライアント・リスナーをドラッグしてイベントが発生するコンポーネントの子としてドロップし、clientListener
を登録します。
注意: カスタム・クライアント・イベントを発生させるコンポーネントでは、クライアント側で生成されたコンポーネントが使用可能になるよう、 |
「クライアント・リスナーの挿入」ダイアログでメソッドを入力し、JavaScript関数のタイプを入力します。スクリプトがページに含まれていない場合はライブラリ名を必ず含めます。タイプはカスタム・イベントの識別に使用される任意の文字列が可能で、たとえば、File ExplorerではenterPressedOnSearch
が使用されています。
コンポーネント・パレットの「操作」パネルからサーバー・リスナーをドラッグしてclientListener
タグの兄弟としてドロップし、サーバー・リスナーを登録します。
「サーバー・リスナーの挿入」ダイアログで、enterPressedOnSearch
などのこのサーバー・リスナーに対する値としてクライアント・リスナーのタイプ値に使用される文字列を入力します。
プロパティ・インスペクタで、2で作成したメソッドに解決される式をmethod
属性に入力します。
実行時、[Enter]キーを押すなど、ユーザーによってイベントが発生すると、クライアント・リスナー・スクリプトが実行されます。このスクリプトでAdfCustomEvent.queue()
メソッドをコールし、指定したイベント・タイプのカスタム・イベントが入力コンポーネントのキューに置かれます。入力コンポーネントに登録されているサーバー・リスナーがカスタム・イベントを受け取り、関連付けられているBeanメソッドが実行されます。
マーシャリングとアンマーシャリングとは、プログラミング言語のデータ・オブジェクトをバイト・ストリームに変換し、同じプログラミング言語または別のプログラミング言語にネイティブのデータ・オブジェクトに戻すプロセスです。ADF Facesでは、マーシャリングとアンマーシャリングは、クライアント・エンドのJavaScriptとサーバー・エンドのJavaとの間で適切にやりとりできるようデータを適切な形式に変換することです。クライアントがブラウザベースの場合、JavaScript Object Notation(JSON)およびXMLの2つが一般的なマーシャリング方法です。ADF Facesでは両方の方法を組み合せて使用し、情報は、サーバーからクライアントには主にJSONで、クライアントからサーバーにはXMLで送信されます(JSONの詳細は、http://www.json.org
を参照してください)。
JavaScriptからJavaへ情報を送信する場合、JavaScriptデータ・オブジェクトはXMLに変換され(マーシャリング)、サーバー側で解析してJavaオブジェクトに戻されます(アンマーシャリング)。たとえば、cmd
というIDのcommandButton
コンポーネントを含むJSFページを考えてみます。ユーザーがcommandButton
コンポーネントをクリックしたとき、このcommandButton
によってactionEvent
が発生したことをクライアントからサーバーに通信する必要があります。requestParameter
マップで、情報がevent + . + id
(id
はコンポーネントのID)という形式を使用したキーにマップされます。つまり、commandComponent
のrequestParameter
マップ・キーは、キーevent.cmd
の値として格納されているXML文字列です。
この例でマーシャリング後のXMLフラグメントは次のようになります。
<m xmlns="http:/oracle.com/richClient/comm"><k v="type"><s>action</s></k></m>
例のm
は、これがマップにアンマーシャルされることを表します。k
はキーであることと、値がString
型であることを表します。サーバー側でXMLフラグメントが、type
(java.lang.String
)をキーとし、action
(java.lang.String
)を値とする1つのエントリのjava.util.Map
に解析されます。
アンマーシャリングされた情報は、クライアントIDごとにまとめられ、リクエスト・マップに格納されてコンポーネントのデコード時に使用されます。この例では、commandButton
がデコードされるとき、クライアントID(event.cmd
)を使用してクライアント・イベントがあるかどうかが確認され、検出された場合はアクションがキューイングされます(デコード動作は、commandButton
コンポーネントのレンダラ階層で実装されます)。
表5-5に、JavaScript型と対応するJava型のマッピングを示します。
表5-5 JavaScriptとJavaの型マップ
JavaScript型 | Java型 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
JavaからJavaScriptへのマーシャリングは、ほとんどの場合JSONを使用して行われます。JSONがJavaScriptでのオブジェクト・リテラル表記法であるため、この種のマーシャリングは明らかです。クライアント・コンポーネントのプロパティは通常JSONでエンコードされます。次の例を考えてみます。
new AdfRichCommandButton('demoTemplate:richComand' {'partialSubmit':true,'useWindow':false})
2番目の引数({'partialSubmit':true,'useWindow':false})はJSONオブジェクトです。JSONがオブジェクトとして直接JavaScript環境に解析されるため、ブラウザ・エンドでの追加のアンマーシャリング処理が不要です。
表のエンコードでも、JSONを使用してプッシュ・メッセージをクライアントに渡します。エンコードされた1つのプッシュ・メッセージを含むエンベロープの例を次にしまします。
[{'rKey':'0','type':'update','data':[{'val':'Active Data Every Second: on row 0:78','prop':'value','cInd':0},{'val':'Active Data Every Second: on row 0:78','prop':'value','cInd':1}]}]
エンベロープは、1つのオブジェクトのみを含むJavaScript Array
で、メッセージを表します。このメッセージには、変更のタイプ、データの実際の値などの情報が含まれ、クライアント側の表ピアで使用され、表自体が更新されます。
表5-6に、Java型と対応するJavaScript型のマッピングを示します。
表5-6JavaとJavaScriptの型マップ
Java型 | JavaScript型 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
変換処理中に情報が失われる可能性あることに注意してください。たとえば、次の例に示すように、カスタム・イベントを使用して数値1
と文字列test
を送信するとします。
AdfCustomEvent.queue(event.getSource(), "something", {first:1, second:"test"});
JavaScriptからJavaへの変換では数値はDoubles
に変換されるため、サーバー側リスナーでfirst
パラメータの型がjava.lang.Double
になります。ただし、初めはサーバー側でint
だったパラメータが、JavaからJavaScriptへの変換で数値になった可能性もあります。サーバーへ戻る際にDouble
に変換されます。
ExtendedRenderKitService
クラスを使用すると、アクション・メソッド・バインディングの起動後などのイベント・レスポンスにJavaScriptを追加できます。これは、ユーザーにデータベース接続が確立できないことを通知するアラートの送信などの簡単なメッセージの場合もあれば、プログラムからポップアップ・ダイアログを終了させるためのポップアップ・ウィンドウでのhide()
などの関数のコールの場合もあります。
たとえば、File ExplorerアプリケーションでユーザーがUpOneFolder
ナビゲーション・ボタンをクリックしてフォルダ構造を上に移動すると、フォルダ・ペインが再描画されて、選択した親フォルダが表示されます。UpOneFolder
ボタン・イベントのクリックを受けてHandleUpOneFolder()
メソッドがコールされます。ExtendedRenderKitService
クラスを使用してJavaScriptがレスポンスに追加されます。
例5-15に、actionListener
属性が、ボタンがクリックされたときにアクション・イベントを処理するHandleUpOneFolder()
ハンドラ・メソッドにバインドされているページのUpOneFolder
コードを示します。
例5-15 JavaScriptをレスポンスに追加するメソッドの起動
<af:commandToolbarButton id="upOneFolder"
. . .
actionListener="#{explorer.headerManager.handleUpOneFolder}"/>
例5-16に、ExtendedRenderKitService
クラスを使用するhandleUpOneFolder
メソッドを示します。
例5-16 JavaScriptのレスポンスへの追加
public void handleUpOneFolder(ActionEvent actionEvent) { UIXTree folderTree = feBean.getNavigatorManager().getFoldersNavigator().getFoldersTreeComponent(); Object selectedPath = feBean.getNavigatorManager().getFoldersNavigator().getFirstSelectedTreePath(); if (selectedPath != null) { TreeModel model = _feBean.getNavigatorManager().getFoldersNavigator().getFoldersTreeModel(); Object oldRowKey = model.getRowKey(); try { model.setRowKey(selectedPath); Object parentRowKey = model.getContainerRowKey(); if (parentRowKey != null) { folderTree.getSelectedRowKeys().clear(); folderTree.getSelectedRowKeys().add(parentRowKey); // This is an example of how to force a single attribute // to rerender. The method assumes that the client has an optimized // setter for "selectedRowKeys" of tree. FacesContext context = FacesContext.getCurrentInstance(); ExtendedRenderKitService erks = Service.getRenderKitService(context, ExtendedRenderKitService.class); String clientRowKey = folderTree.getClientRowKeyManager(). getClientRowKey(context, folderTree, parentRowKey); String clientId = folderTree.getClientId(context); StringBuilder builder = new StringBuilder(); builder.append("AdfPage.PAGE.findComponent('"); builder.append(clientId); builder.append("').setSelectedRowKeys({'"); builder.append(clientRowKey); builder.append("':true});"); erks.addScript(context, builder.toString()); } } finally { model.setRowKey(oldRowKey); } // Only really needed if using server-side rerendering // of the tree selection, but performing it here saves // a roundtrip (just one, to fetch the table data, instead // of one to process the selection event only after which // the table data gets fetched!) _feBean.getNavigatorManager().getFoldersNavigator().openSelectedFolder(); } }
クライアント側での動作を実装する場合、通常はJavaScriptを記述し、それをクライアント・リスナーとしてコンポーネントに登録する必要がありますが、ADF Facesのクライアント動作タグを使用すると、一般的なクライアント操作を宣言的に実行できます。JavaScriptコードを記述するかわりにこれらのタグを使用して同じ処理を実装すると、ブラウザにダウンロードする必要のあるJavaScriptコードの量が少なくなります。
ADF Facesでは、クライアント・リスナーのかわりに使用できるクライアント動作タグが用意されています。
panelDashboardBehavior
: 応答性を向上させるために、実行時にpanelDasboard
コンポーネントに子コンポーネントを挿入できます。詳細は、8.8.1項「panelDashboardコンポーネントの使用方法」を参照してください。
insertTextBehavior
: コマンド・コンポーネントで、inputText
コンポーネントのカーソル位置にテキストを挿入できます。詳細は、9.3.2項「inputTextコンポーネントへのテキストの挿入機能の追加方法」を参照してください。
richTextEditorInsertBehavior
: コマンド・コンポーネントで、richTextEditor
コンポーネントのカーソル位置にテキスト(事前フォーマット済テキストを含む)を挿入できます。詳細は、9.8.2項「richTextEditorコンポーネントへのテキストの挿入機能の追加方法」を参照してください。
showPopupBehavior
: コマンド・コンポーネントでポップアップ・コンポーネントを起動できます。詳細は、13.4項「ポップアップ要素の起動」を参照してください。
showPrintablePageBehavior
: コマンド・コンポーネントで、ページの印刷可能版を生成して表示できます。詳細は、35.2項「印刷用のページの表示」を参照してください。
scrollComponentIntoViewBehavior
: クリックして、コマンド・コンポーネントから名前付きのコンポーネントに移動できます。詳細は、5.6.1項「scrollComponentIntoViewBehaviorタグの使用方法」を参照してください。
クライアント動作タグで、サーバー側イベント配信が自動的に取り消されます。したがって、親コンポーネントのactionListener
またはaction
属性が無視されます。これは無効にすることができません。サーバー側の機能もトリガーする場合は、クライアント側イベントを使用する(5.3項「ADF Facesクライアント・イベントに対するJavaScriptの使用」を参照)か、AdfCustomEvent
およびaf:serverListener
を使用してサーバー側イベントを配信するクライアント・リスナーを追加します(5.4項「「クライアントからサーバーへのカスタム・イベントの送信」を参照)。
ユーザーがページの特定のコンポーネントに移動できるようにする場合は、scrollComponentIntoViewBehavior
タグを使用します。このアクションは、HTMLのアンカーと似ています。たとえば、commandLink
コンポーネントを使用して、ユーザーがページの特定の部分に移動できるようにできます。richTextEditor
およびinlineFrame
コンポーネントの場合は、サブコンポーネントに移動できます。たとえば、図5-1は、そのテキストに多数のセクションがあるrichTextEditor
コンポーネントを示しています。エディタの下にあるコマンド・リンクを使用すると、テキストの特定部分に移動できます。
ユーザーがスクロールしたコンポーネントにフォーカスを切り替えるためのタグを構成することもできます。
scrollComponentIntoViewBehaviorタグを使用する手順:
ユーザーがクリックして、指定されたコンポーネントに移動するコマンド・コンポーネントを作成します。手順は、18.2.1項「コマンド・ボタンおよびコマンド・リンクの使用方法」を参照してください。
コンポーネント・パレットの「操作」セクションから、「コンポーネントのビュー内へのスクロール動作」をコマンド・コンポーネントの子としてドラッグ・アンド・ドロップします。
「コンポーネントのスクロール表示動作の挿入」ダイアログで、ドロップダウン矢印を使用して「編集」を選択し、移動してユーザーの移動先であるコンポーネントを選択します。
移動後にコンポーネントにフォーカスを設定する場合は、プロパティ・インスペクタで、focus
属性をtrue
に設定します。
richTextEditor
またはinlineFrame
コンポーネントの場合は、オプションで、subTargetId
属性の値を入力します。このIDは、richTextEditor
またはinlineFrame
コンポーネントの値で定義されています。
たとえば、図5-1に示されているscrollComponentIntoViewBehavior
タグのsubTargetId
属性の値は、Introduction
です。richTextEditor
の値は、例5-17に示されているプロパティにバインドされています。Introduction
は最初のヘッダーのIDです。
例5-17 プロパティに定義されているsubTargetId値
private static final String _RICH_SECTIONED_VALUE = "<div>\n" + " <h2>\n" + " <a id=\"Introduction\"></a>Introduction</h2>\n" + " <p>\n" + " The ADF Table component is used to display a list of structured data. For example,\n" + " if we have a data structure called Person that has two properties - firstname and\n" + " lastname, we could use a Table with two columns - one for firstname, and the other\n" + " for lastname - to display a list of Person objects.\n" + " </p>\n" + " </div>\n" + " <div>\n" + " <h2>\n" + " <a id=\"The_Table_Model\"></a>The Table Model</h2>\n" + " <p>\n" + . . . </div>";
ADF Facesでは、pollEvent
を使用して、指定した間隔でサーバーと通信できるポーリング・コンポーネントが提供されます。たとえば、ポーリング・コンポーネントを使用して、outputText
コンポーネントを更新したり、サーバーにハートビートを配信して、それらのセッションがタイムアウトするのを防ぐことができます。
ポーリング時に必要な処理を実行するのに使用されるpollEvent
に、リスナーを作成する必要があります。たとえば、ポーリング・コンポーネントを使用してoutputText
コンポーネントの値を更新する場合は、データ・ソースの値をチェックしてからコンポーネントを更新するpollEventListener
メソッドを実装します。
間隔時間を設定し、ポーリング・コンポーネントがそのポーリング・イベントを配信する頻度を決定できます。ページがタイムアウトするまでの時間を設定することもできます。これは、ページでのポーリングによりセッションがタイムアウトしないようにする場合に便利です。リクエストがサーバーに送信されるたびに、セッションのタイムアウト値がページに書き込まれ、セッションをいつタイムアウトするかが決定されます。ポーリング・コンポーネントは(間隔時間に基づいて)サーバーにリクエストを送信し続けるため、セッションがタイムアウトすることはありません。これは、ネットワークの使用とメモリーの両方で非効率になります。
この問題を回避するには、web.xml
構成ファイルにoracle.adf.view.rich.poll.TIMEOUT
コンテキスト・パラメータを含めます(タイムアウトするまでのページの実行時間を指定します)。キーボードまたはマウスのアクティビティがない場合、ページはタイムアウトできるとみなされます。デフォルトのタイムアウト時間は10分です。このため、ユーザーのアクティビティが10分間ない場合(キーボードやマウスを使用しない時間)は、フレームワークによりポーリングが停止され、その時点から、ページは標準のサーバ側セッション・タイムアウトに入ります(詳細は、A.2.3.22項「セッション・タイムアウトの警告」を参照)。
アプリケーションがタイムアウトした場合、ユーザーがマウスを移動したり、キーボードを再度使用すると、新しいセッション・タイムアウト値がページに書き込まれ、ポーリングが再開します。
ポーリング・コンポーネントのtimeout
属性を使用して、特定のページでこのタイムアウトをオーバーライドできます。
ポーリング・コンポーネントを使用する場合、ポーリング・イベントの機能を処理するには、通常ハンドラ・メソッドも作成します。
開始前
属性が機能に与える影響について理解しておくと役立ちます。詳細は、5.7項「ポーリング・イベントを使用したページの更新」を参照してください。
ポーリング・コンポーネントを使用する手順:
マネージドBeanで、ポーリング・コンポーネントのハンドラを作成します。マネージドBeanの詳細は、2.6項「マネージドBeanの作成と使用」を参照してください。
コンポーネント・パレットの「操作」パネルから、「ポーリング」をドラッグ・アンド・ドロップして、ポーリング・コンポーネントを作成します。
プロパティ・インスペクタで、「共通」セクションを開いて次の設定を行います。
Interval: ポーリング・イベント間の時間を入力します(ミリ秒)。ポーリングを無効にするには0に設定します。
PollListener: 手順1のメソッドに評価されるEL式を入力します。
web.xml
ファイルのグローバル・タイムアウト値をオーバーライドする場合は、「その他」セクションを開き、「タイムアウト」に、ページがポーリングを停止してセッションがタイムアウトするまでの時間(ミリ秒)を設定します。