この章の内容は次のとおりです。
ADF Facesは、JavaServer Facesアーキテクチャを拡張し、標準のサーバー中心モデルの上にクライアント側フレームワークを追加しています。ADF Facesコンポーネントの大部分は、リクエストのためにサーバー側で生成されるHTMLにレンダリングされます。また、ADF Facesでは、クライアント側コンポーネントとイベント・モデルを使用してクライアントに到達できるようにコンポーネントを実装できます。
ADF Facesフレームワークには、JavaScriptを使用するために通常必要な機能の多くがすでに含まれています。多くの場合、JavaScriptを使用しなくても、リッチなコンポーネント機能を宣言的に実現できます。ただし、クライアント側イベントに対するカスタム処理など、独自のJavaScriptを追加する必要がある場合があります。このような場合は、クライアント側フレームワークを使用できます。
最も多く対話対象となるJavaScriptクラスはAdfUIComponent
とそのサブクラスです。このクラスのインスタンスは、サーバー側コンポーネントのクライアント側表現です。クライアント側コンポーネントは、イベント処理がサポートされる簡単なプロパティ・コンテナとみなすことができます。クライアント側コンポーネントは、主に、アプリケーション開発者とフレームワーク自体の両方に対するAPI規定を公開することでページに動作を追加するためにあります。たとえば、クライアントでのボタンの状態の切替えを可能にするのは、この規定です。
各クライアント・コンポーネントには、プロパティ(キーと値のペア)のセットと、サポートされている各イベント・タイプ用のリスナーのリストがあります。すべてのADF Faces JavaScriptクラスは、他のJavaScriptライブラリと名前が競合しないようAdf
で始まります。たとえば、RichButton
はAdfButton
を持ち、RichDocument
はAdfRichDocument
を持つなどです。
クライアント側JavaScriptレイヤーでは、クライアント・コンポーネントは主にフレームワークおよび開発者にAPI規定を提供するために存在します。クライアント・コンポーネントは、状態を格納し、APIを提供するためにのみ存在するため、DOM (Document Object Model)と直接対話しません。DOMとのすべての対話は、ピアと呼ばれる媒介を使用して行われます。ピアでは、Javaレンダラによって生成されたDOMと対話し、状態の更新とユーザーとの対話へのレスポンスが処理されます。
ピアでは、この他に次のような多くの処理が行われます。
DOMの初期化とクリーンアップ
DOMのイベント処理
ジオメトリ管理
部分ページ・レスポンスの処理
子の可視性の変更の処理
この分離により、コンポーネントおよびアプリケーション開発者がコンポーネントのDOM実装の変更から隔離され、コンポーネント(たとえば、Flashコンポーネント)がHTML DOMで実装されているかどうかをアプリケーションが知る必要もまったくなくなります。
大部分のコンポーネントベースのフレームワーク同様、JSFのコンポーネント・モデルに固有の特性は、コンポーネントはネストして階層(通常コンポーネント・ツリーと呼ばれる)を形成できるということです。これは、親コンポーネントで子が追跡され、コンポーネント・ツリーをたどって特定のコンポーネントの子孫をすべて見つけることができるということです。コンポーネント・ツリー全体はサーバーにあり、ADF Facesクライアント側コンポーネント・ツリーにはあまり移入されません。
パフォーマンスを最適化するために、クライアント・コンポーネントは、clientListener
ハンドラが登録されているため、またはページ開発者がクライアント側のコンポーネントと対話する必要があり、クライアント・コンポーネントを使用可能として構成したため必要になった場合にのみ存在します。例外的なケースを除き、クライアント・フレームワークを理解する必要はありません。アーキテクチャ機能の大部分は、コードを作成せずに宣言的に使用できます。
たとえば、フレームワークでは、各サーバー側コンポーネントに対応するクライアント・コンポーネントをすべて作成するわけではないため、クライアント版のコンポーネント・インスタンスが必要な場合もあります。「クライアント側コンポーネントのインスタンス化」に、これを宣言的に行う方法が説明されています。JDeveloperの「プロパティ」ウィンドウを使用して、「レンダリングおよび可視性の理解」で説明されている、コンポーネントをレンダリングするかどうか、あるいは非表示にするかどうかを決めるプロパティを設定します。
注意:
既存のサーバー側コンポーネントに対応しないJavaScriptコンポーネントが存在することも可能です。たとえば、一部のADF Facesコンポーネントでは、ポップアップ・コンテンツが必要なクライアント側動作を持ちます。これらのコンポーネントでは、サーバー側Java RichPopup
コンポーネントが存在しなくても、AdfRichPopup
JavaScriptコンポーネントを作成できます。
その他の機能では、ADF Faces JavaScript APIの使用が必要な場合があります。たとえば、「ページでのクライアント・コンポーネントの検索」では、APIを使用して特定のクライアント側コンポーネントを探す方法を説明し、「クライアントでのコンポーネント・プロパティへのアクセス」では、特定のプロパティにアクセスする方法を説明しています。
JavaScriptを多用したフレームワークに共通する問題は、大規模なJavaScriptコード・ベースをクライアントに送信する最良の方法を決定することです。すべてのコードが単一のJavaScriptライブラリにある場合はダウンロード時間が長くなりますが、JavaScriptを多くのライブラリに分割しすぎると、ラウンドトリップが大量になります。この問題を軽減するために、ADF FacesではJavaScriptコードがパーティションに集約されます。JavaScriptライブラリ・パーティションには、コンポーネントのコードまたは通常一緒に使用される機能、あるいはその両方が含まれています。詳細は、「JavaScriptライブラリのパーティション化」を参照してください。
インラインJavaScriptをページに直接追加するか、JavaScriptライブラリをページにインポートできます。ライブラリをインポートする場合は、ページ・コンテンツ・サイズが削減され、ライブラリをページ間で共有でき、ブラウザによってキャッシュできます。可能な場合には必ずJavaScriptライブラリをインポートする必要があります。インラインJavaScriptは、ページ固有の小さなスクリプトが必要な場合にのみ使用します。
パフォーマンスに関するヒント
インラインJavaScriptは、レスポンス・ペイロード・サイズを増加させる可能性があり、ブラウザにまったくキャッシュされないため、ブラウザのレンダリングをブロックすることがあります。インラインJavaScriptを使用するかわりに、すべてのスクリプトをJavaScriptライブラリに配置することを検討してください。
インラインJavaScriptを使用する必要がある場合、必要とするページにのみこれを含めます。ただし、ほとんどのページで同じJavaScriptコードが使用されている場合、スクリプトを含めることを検討するか、テンプレートにライブラリをインポートしてください。
JavaScriptコード・ライブラリが非常に大きくなった場合、ライブラリを内容に応じて分割し、(テンプレートに配置しないで)ページで必要な部分のみを含めることに注意してください。この方法で、ブラウザ・キャッシュが使用され、ページのHTMLコンテンツが小さくなるため、パフォーマンスが向上します。
JavaScriptをページに作成し、clientListener
タグを使用して呼び出します。
始める前に:
ページへのJavaScriptの追加に関する知識が役立つ場合があります。詳細は、「JavaScriptのページへの追加」を参照してください。
インラインのJavaScriptを使用する手順:
af:resource
タグを使用して、ページからJavaScriptライブラリにアクセスします。このタグは、document
タグのmetaContainer
ファセットの内部に含める必要があります。
始める前に:
ページへのJavaScriptの追加に関する知識が役立つ場合があります。詳細は、「JavaScriptのページへの追加」を参照してください。
ページからJavaScriptライブラリにアクセスする手順:
JavaScriptでクライアント・コンポーネントにアクセスする必要がある場合、リスナーのコンテキストで行われ、イベントのソース・コンポーネントにアクセスする必要がある場合がしばしばあります。getSource()
メソッドを使用してクライアント・コンポーネントを取得します。次の例に、名前を表示するためにソース・クライアント・コンポーネントにアクセスするsayHello
関数を示します。
function sayHello(actionEvent) { var component=actionEvent.getSource(); //Get the ID for the component var id=component.getId(); alert("Hello from "+id); }
クライアント・イベント・ソースへのアクセスの詳細は、「ADF Facesクライアント・イベントに対するJavaScriptの使用」を参照してください。クライアント側プロパティへのアクセスの詳細は、「クライアントでのコンポーネント・プロパティへのアクセス」を参照してください。実行時のクライアント・イベントの処理の詳細は、「実行時の処理: クライアント側イベントの機能の仕方」を参照してください。
デフォルトでは、フレームワークでは、どのコンポーネントが、対応するクライアント側コンポーネント・インスタンスを持つかについて保証されません。クライアント上のコンポーネントと対話するには、通常はclientListener
ハンドラを登録します。登録されているclientListener
ハンドラがコンポーネントにある場合、コンポーネントはクライアント側表現を自動的に持ちます。clientComponent
属性をtrue
に設定して、コンポーネントがクライアントで使用できるよう明示的に構成することもできます。
clientComponent
属性を使用してクライアント側インスタンスを持つようにコンポーネントを手動で構成できます。
パフォーマンスに関するヒント
クライアントのコンポーネントとプログラムで対話する場合にのみclientComponent
をtrue
に設定します。
注意:
フレームワークが独自に使用するためにクライアント・コンポーネントを作成する場合、そのクライアント・コンポーネントにはその時点でフレームワークが必要とする情報のみ含まれます。たとえば、一部の属性は使用可能でない場合があります。
始める前に:
クライアント側インスタンスに関する知識が役立つ場合があります。詳細は、「クライアント側コンポーネントのインスタンス化」を参照してください。
クライアント側インスタンスのコンポーネントを構成する手順:
true
に設定します。clientComponent
属性をtrue
に設定すると、コンポーネントのAdfUIComponent
クラスのインスタンスがフレームワークで作成されます。このクラスでは、クライアント側で操作できるAPIが提供され、基本的なプロパティ・アクセッサ・メソッド(getProperty()
、setProperty()
など)、イベント・リスナーの登録およびイベント配信関連のAPIも提供されます。フレームワークには、プロパティ固有のアクセッサ・メソッド(getText()
、setText()
など)を公開するレンダラ固有のサブクラス(AdfRichOutputText
など)も用意されています。これらのアクセッサ・メソッドは、AdfUIComponent
クラスのgetProperty()
およびsetProperty()
メソッドをラップするのみのラッパーで、コーディングが簡便になるよう用意されています。
たとえば、sayHello
関数から値(表示するテキスト)を取得するoutputText
コンポーネントがページにあるとします。この関数は、値を設定するためにoutputText
コンポーネントにアクセスできる必要があります。次のJSFページ・コードに示すように、これが機能するには、クライアント側バージョンのoutputText
コンポーネントがある必要があります。outputText
コンポーネントにid
値があり、clientComponent
属性がtrue
に設定されていることに注意してください。また、値はJavaScriptで設定されるため、例には値がないことにも注意してください。
<af:button text="Say Hello"> <af:clientListener method="sayHello" type="action"/> </af:button> <af:outputText id="greeting" value="" clientComponent="true">
outputText
にクライアント側表現があるため、JavaScriptでこれを見つけて使用できます。
注意:
クライアント・リスナーがない場合やclientComponent
属性がtrueに設定されていない場合でも、ADF Facesフレームワークは自身の目的のためにクライアント・コンポーネントを作成することがあります。ただし、これらのクライアント・コンポーネントは完全に機能しない場合があります。
従来のJSFアプリケーションでは、クライアントでイベントを処理する場合、DOMレベルのイベントをリスニングする必要があります。ただし、これらの実装は移植可能な形式で配信されません。ADF Facesのクライアント側イベント・モデルはJSFイベント・モデルと似ていますが、クライアントで実装されます。クライアント側イベント・モデルはDOMの要約で、コンポーネントレベルのイベント・モデルとライフサイクルが提供され、サーバーから独立して実行されます。このため、ボタンのclick
イベントをリスニングする必要がありません。かわりに、キーまたはマウスのイベントで発生するAdfActionEvent
イベントをリスニングできます。
クライアントによって送信されるイベントはすべてAdfBaseEvent
クラスのサブクラスです。各クライアント・イベントには、イベントをトリガーしたコンポーネントであるソースがあります。イベントには、イベントをリスニングするリスナーを判断するタイプ(action
またはdialog
など)もあります。クライアント・リスナーは、af:clientListener
タグを使用してコンポーネントに宣言的に登録します。
af:clientListener
タグを使用して、クライアント・イベントに対して対応するJavascriptをコールします。たとえば、クリックされたら"Hello World"アラートを表示する必要のあるボタンがあるとします。最初に、アラートを表示することでイベントに応答するJavaScript関数を作成する必要があります。次に、その関数を呼び出すクライアント・リスナーをコンポーネントに追加します。
始める前に:
クライアント・イベント処理に関する知識が役立つ場合があります。詳細は、「クライアント・イベントのリスニング」を参照してください。
クライアント・イベントをリスニングする手順:
ヒント:
ボタンにクライアント・リスナーが登録されているため、フレームワークで、クライアント版のコンポーネントが自動的に作成されます。
クライアント版のコンポーネントがあるため、ボタンがクリックされると、Adf
Action
クライアント・イベントが起動されます。AdfAction
イベントをリスニングするようclientListener
タグが構成されているため、sayHello
関数が実行されます。クライアント側イベントの詳細は、「ADF Facesクライアント・イベントに対するJavaScriptの使用」を参照してください。
ADF FacesアプリケーションでJSFページを表示するブラウザを閉じようとするときに、ネイティブ・ブラウザの確認ダイアログを表示してナビゲーションを確認することにより、ページからの移動を制御できます。このタイプのイベントを処理するために、af:document
コンポーネンはタイプbeforeunload
のADFクライアント・リスナーの呼出しをサポートし、ADF Facesアプリケーションが、一部のブラウザで問題の原因となる可能性のある、実行順序の未定義にならないようにします。特に、このタイプのナビゲーション・イベントを、window.addEventListener("beforeunload", function(e) {...}
などのブラウザのネイティブのbeforeunload
ハンドラを使用して処理しないでください。
Oracle ADFのonbeforeunload
イベントのサポートを使用して、af:document
コンポーネントでイベント・ハンドラをコールするクライアント・イベント・リスナーを定義できます。ADFイベント・ハンドラは現在のページから離れるまたはリロードが発生する前に呼び出され、ユーザーはADF Facesフレームワークの実行の中で完全な終了をキャンセルすることができます。ユーザーがページからの移動を継続する場合、ブラウザによりwindow.unload
イベントが発生し、ADF Facesアプリケーションは終了します。
たとえば、クライアント・リスナーはブラウザのネイティブのbeforeunload
ハンドラ用のハンドラの動作と似たような方法でADF beforeunload
イベントを処理でき、ユーザーに終了のキャンセルを求める文字列を返す必要があります。
... <af:document> <af:resource type="javascript"> function myBeforeUnload(beforeunloadEvent) { return "Are you sure that you want to leave?" } </af:resource> <af:clientListener type="beforeunload" method="myBeforeUnload"/>
ADF beforeunload
ハンドラが呼び出されたとき、未定義以外の値が返されると、現在のページから実際に移動するのかどうかを尋ねる、ブラウザ制御の警告ダイアログが表示されます。ブラウザによっては、返される文字列は、警告ダイアログの一部として表示されることがあります。警告メッセージは、ハンドラに渡されたADF beforeunload
オブジェクトでsetWarningMessage
メソッドを呼び出すことによっても設定できます。
コンポーネントの各組込みプロパティには、簡易アクセッサ・メソッドがコンポーネント・クラスに用意されています。たとえば、クライアント・コンポーネントでgetValue()
メソッドをコールし、サーバーで使用されたのと同じ値を受け取ることができます。
注意:
ADF Faces内のブール・プロパティを含むすべてのクライアント・プロパティでgetXyz
関数命名規則を使用します。ブール・プロパティのisXyz
命名規則は使用されません。
定数もクラス・オブジェクトのプロパティ名に使用できます。たとえば、styleClass
を使用するかわりにAdfRichDialog.STYLE_CLASS
定数を使用できます。
注意:
一部のJavaScript実行環境では、文字列をコーディングすると呼出しのたびにオブジェクトを割り当てる必要があるため、JavaScriptでは、文字列をコーディングするよりも定数を参照する方が効率的です。
コンポーネントのプロパティが変更されると、最終的な結果として、コンポーネントのDOMが新しい状態を反映するように更新されます。場合によっては、サーバーへのラウンドトリップは発生しません。このプロセスにおけるコンポーネントのロールはかなり限られています。新しいプロパティ値を格納してから、変更をピアに通知するだけです。ピアには、新しいコンポーネント状態を反映するようにDOMを更新するロジックが含まれます。
注意:
すべてのプロパティの変更が、クライアント側のピアを使用して処理されるわけではありません。一部のプロパティ変更はサーバーに伝播され、PPRを使用してコンポーネントがレンダリングされます。
クライアントに設定されるほとんどのプロパティ値は、サーバーとの自動同期が行われる原因になります(ただし、一部の複合Javaオブジェクトはクライアントにまったく送信されません)。ただし、これとは異なる動作をするセキュア・プロパティと分離プロパティの2種類のプロパティがあります。
セキュア・プロパティは、クライアントにまったく設定できないプロパティです。たとえば、悪意のあるクライアントでJavaScriptを使用してLink
コンポーネントのimmediate
フラグがtrue
に設定されるとします。この変更がサーバーに伝播された結果、サーバー側の検証が省略され、セキュリティ・ホールとなる可能性があります(immediate
プロパティの使用の詳細は、「immediate属性の使用」を参照してください)。つまり、immediate
プロパティはセキュア・プロパティです。JavaScriptからセキュア・プロパティの設定を試みると失敗します。詳細は、「無効なプロパティを保護しない方法」を参照してください。
ADF Facesでは、disabled
プロパティを非保護にできるように構成できます。この機能は、JavaScriptを使用してボタンを有効および無効にする必要がある場合に役立ちます。ADF Facesセキュア・クライアントプロパティのリストは、「セキュア・クライアント・プロパティ」を参照してください。
分離プロパティは、クライアントで設定できるがサーバーに伝播することはできないプロパティです。これらのプロパティには、サーバーのライフサイクルに依存しないクライアント上のライフサイクルがあります。たとえば、クライアント・フォーム入力コンポーネント(AdfRichInputText
など)には、JavaのEditableValueHolder
コンポーネントのようにsubmittedValue
プロパティがあります。ただし、このプロパティの設定はサーバーに直接影響しません。この場合、標準のフォーム送信手法によって、送信された値のサーバーでの更新を処理します。
プロパティは、分離とセキュアの両方にすることができます。実際には、このようなプロパティはクライアント上で分離プロパティのように動作します。これらはクライアントで設定できますが、サーバーには送信されません。ただし、サーバーではセキュア・プロパティとして動作するため、クライアントがこれらを設定しようとすると拒否されます。
セキュア・プロパティは、クライアントでは設定できず、セキュリティ・ホールを防ぐプロパティです。表4-1に、クライアント・コンポーネントのセキュア・プロパティを示します。
表4-1 セキュア・クライアント・プロパティ
コンポーネント | セキュア・プロパティ |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ADF Facesフレームワークには、基礎となるADFUIComponent.setProperty
関数をコールして適切なプロパティ名を渡す便利なsetXYZ
関数が用意されています(詳細は、Oracle ADF Faces JavaScript APIリファレンスを参照してください)。次のコードに、setProperty
関数を使用して、値が変更されたときにinputText
コンポーネントのbackgroundcolor
プロパティを赤に設定する方法を示します。
<af:form> <af:resource type="javascript"> function color(event) { var inputComponent = event.getSource(); inputComponent.setproperty("inlineStyle", "background-color:Red"); } </af:resource> <af:outputText id="it" label="label"> <af:clientListener method="color" type="valueChange"/> </af:inputText> </af:form>
これらの関数を使用してプロパティの値を変更できます。分離プロパティまたはセキュア・プロパティでないかぎり、値はサーバー上でも変更されます。
クライアントでsetProperty()
関数をコールすると、プロパティが新しい値に設定され、同時にPropertyChangeEvent
イベントが古い値と新しい値を使用して起動されます(値が異なる場合)。また、プロパティを設定すると、コンポーネントが自身を再レンダリングする場合があります。
デフォルトでは、無効化されたプロパティは保護されたプロパティです。つまり、JavaScriptを使用してそれをクライアント上に設定することはできません。ただし、disabled
プロパティを非保護に設定するには、unsecured
プロパティを使用します。disabled
プロパティを非保護にする必要のあるコンポーネントのコードに、このプロパティとdisabled
の値を手動で追加する必要があります。たとえば、disabled
プロパティを非保護にする必要のあるボタンのコードは次のようになります。
<af:button text="commandButton 1" id="cb1" unsecure="disabled"/>
disabled
属性が安全な場合、アプリケーションはdisabled
属性により、アクション・イベントが配布されるべきでないときには配布されないようにします。disabled属性を非保護にすると、サーバーはそのdisabled属性を正しいものとして信頼できなくなります。このため、ユーザーはactionListenersを使用して同等のチェックを実行する必要があります。
たとえば、支出承認ページがあり、そのページで特定のマネージャが$200未満の請求書のみ承認できるようにするとします。そのため、現在のユーザーが請求書の承認を許可されていないかぎり、承認ボタンを無効にする必要があります。disabled
属性を非保護にしないと、承認ボタンは、サーバーへのラウンドトリップが発生するまで無効のままになります。現在のユーザーが経費を承認できるかどうかはロジックが決定します。つまり、レンダリングのときに、ボタンが現在のユーザーに対して正しく表示されないことがあります。ページがロードされる際に、ボタンを正しく表示するには、unsecure
属性をdisabled
に設定し、クライアント上でJavaScriptを使用してボタンを無効にするかどうか決定します。ただし、この場合は、任意のJavaScript(制御できない悪意のあるJavaScriptを含む)が同じことを行う可能性があります。
悪意のあるJavaScriptを回避するには、ボタンにactionListener
を使用してサーバー上でロジックを実行します。サーバーにロジックを追加すると、無効な属性を変更してはならない場合に、それが変更されません。支出承認の例では、承認を実行する前にJavaScriptを使用して金額が$200以下であることをチェックし、また、ボタンにactionListener
を使用して現在のマネージャに適切な支出権限があることを再度チェックする必要があります。
同様に、アプリケーションを実行時に変更できるようにし、ユーザーがunsecure
またはdisabled
属性、あるいはその両方を編集できるようにする場合は、サーバーとのラウンドトリップが発生した場合と同じロジックがアプリケーションによって実行されるようにする必要があります。
プロパティの値をサーバーに常に配信して同期することは望ましくない場合があります。たとえば、フォームにinputText
コンポーネントがあり、ユーザーがコンポーネントの1つで値を変更したらすぐに、変更済インジケータを表示するとします。この操作を行うには、JavaScriptを使用して、valueChangeEvent
イベントが配信されたらクライアント・コンポーネントのchanged
属性をtrue
に設定します。また、ユーザーがページを送信した場合は、その時点で値が保存されるため、変更済インジケータを表示しません。
次のように、JavaScriptを使用して、valueChangeEvent
が配信されたらchanged属性をtrueに設定します。
<af:form> <af:resource type="javascript"> function changed(event) { var inputComponent = event.getSource(); inputComponent.setChanged(true); } </af:resource> <af:inputText id="it" label="label"> <af:clientListener method="changed" type="valueChange"/> </af:inputText> <af:button text="Submit"/> </af:form>
コンポーネントのすべてのプロパティは一般にサーバーと同期されるため、この例を使用すると、changed
属性の値(true
)はサーバーにも送信されます。このため、変更済インジケータは引き続き表示されます。
値がサーバーに保存されたときにインジケータを表示しないようにするには、次のいずれかの代替方法を使用できます。
イベント・リスナーを使用して、ロジックをクライアントからサーバーに移動します。次に示すように、この代替方法は、valueChangeEvent
イベントなど、サーバーに配信されるイベントがある場合に使用します。
<af:form> <af:inputText label="label" autoSubmit="true" changed="#{test.changed}" valueChangeListener="#{test.valueChange}"/> <af:button text="Submit" /> </af:form>
次の例に、対応するマネージドBeanコードを示します。
import javax.faces.event.ValueChangeEvent; import oracle.adf.view.rich.context.AdfFacesContext; public class TestBean { public TestBean() {} public void valueChange(ValueChangeEvent valueChangeEvent) { setChanged(true); AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance(); adfFacesContext.addPartialTarget(valueChangeEvent.getComponent()); FacesContext.getCurrentInstance().renderResponse(); } public void setChanged(boolean changed) { _changed = changed; } public boolean isChanged() { return _changed; } private boolean _changed; }
次に示すように、カスタム・サーバー・イベントとserverListener
タグを呼び出すJavaScriptを使用して、ロジックをサーバーに移動します。この方法は、配信されるイベントがない場合に使用します。
<af:form> <af:resource type="javascript"> function changed(event) { var inputComponent = event.getSource(); AdfCustomEvent.queue(inputComponent, "myCustomEvent", null, true); } </af:resource> <af:inputText label="label" changed="#{test2.changed}"> <af:serverListener type="myCustomEvent" method="#{test2.doCustomEvent}"/> <af:clientListener method="changed" type="valueChange"/> </af:inputText> <af:button text="Submit"/> </af:form>
次の例に、マネージドBeanコードを示します。
package test; import javax.faces.context.FacesContext; import oracle.adf.view.rich.context.AdfFacesContext; import oracle.adf.view.rich.render.ClientEvent; public class Test2Bean { public Test2Bean() { } public void doCustomEvent(ClientEvent event) { setChanged(true); AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance(); adfFacesContext.addPartialTarget(event.getComponent()); FacesContext.getCurrentInstance().renderResponse(); } public void setChanged(boolean changed) { _changed = changed; } public boolean isChanged() { return _changed; } private boolean _changed; }
クライアント・コンポーネントでchanged
属性をtrue
に設定し、これがサーバーに伝播されますが、その後コマンド・コンポーネントでactionListener
を使用して、changed
属性をfalse
に戻します。次に、JSFコードを示します。
<af:form> <af:resource type="javascript"> function changed(event) { var inputComponent = event.getSource(); inputComponent.setChanged(true); } </af:resource> <af:inputText binding="#{test3.input}" label="label"> <af:clientListener method="changed" type="valueChange"/> </af:inputText> <af:button text="Submit" actionListener="#{test3.clear}"/> </af:form>
次のサンプルで、対応するマネージドBeanコードを示します。
package test; import javax.faces.event.ActionEvent; import oracle.adf.view.rich.component.rich.input.RichInputText; public class Test3Bean { public Test3Bean() { } public void clear(ActionEvent actionEvent) { _input.setChanged(false); } public void setInput(RichInputText input) { _input = input; } public RichInputText getInput() { return _input; } private RichInputText _input; }
組込みプロパティ以外の情報をクライアントに送信する必要がある場合もあります。これは、ボーナス属性を使用して行えます。ボーナス属性は、clientAttribute
タグを使用してコンポーネントに追加できる追加属性です。パフォーマンス上の理由から、クライアントに送信されるボーナス属性は、clientAttribute
で指定されたもののみです。
clientAttribute
タグでは、サーバー側コンポーネントの属性マップに追加される名前と値のペアを指定します。サーバー側属性マップの移入に加えて、clientAttribute
タグを使用すると、ボーナス属性がクライアントに送信され、AdfUIComponent.getProperty("
bonusAttributeName
")
メソッドを使用してアクセスできます。
フレームワークで、属性値のクライアントへのマーシャリングが行われます。マーシャリング・レイヤーで、文字列、ブール、数値、日付、配列、マップなど、各オブジェクト型のマーシャリングがサポートされます。マーシャリングの詳細は、「データのマーシャリングとアンマーシャリングに関する必知事項」を参照してください。
パフォーマンスに関するヒント
マーシャリングによる過度のオーバーヘッドを防ぐため、クライアント側ボーナス属性を多用しないでください。
注意:
clientAttribute
タグは、(アプリケーションで定義される)ボーナス属性にのみ使用します。クライアントで標準のコンポーネント属性にアクセスする必要がある場合、clientAttribute
タグを使用するのではなく、clientComponent
属性をtrue
に設定します。詳細は、「クライアント側コンポーネントのインスタンス化」を参照してください。
「コンポーネント」ウィンドウを使用してコンポーネントにボーナス属性を追加できます。
始める前に:
ボーナス属性に関する知識が役立つ場合があります。詳細は、「クライアント側コンポーネントに対するボーナス属性の使用」を参照してください。
ボーナス属性を作成する手順:
クライアント側ボーナス属性は、サーバーからクライアントへ自動的に配信されますが、その逆は行われません。つまり、クライアントでのボーナス属性の変更または設定は、サーバーに反映されません。既知(ボーナス以外)の属性のみ、クライアントからサーバーへ同期がとられます。アプリケーションで定義されたデータをサーバーに送り返す必要がある場合、カスタム・イベントを作成します。詳細は、「クライアントからサーバーへのカスタム・イベントの送信」を参照してください。
すべてのADF Faces表示コンポーネントには、コンポーネントをページに表示してユーザーに見えるようにするかどうかに関係するrendered
とvisible
の2つの属性があります。
rendered
属性のセマンティクは非常に厳密です。rendered
がfalse
に設定されている場合、サーバーへのラウンドトリップなしにコンポーネントをクライアントで表示することはできません。ページのコンテンツの動的な表示/非表示をサポートするために、フレームワークにはvisible
属性が追加されています。false
に設定されている場合、コンポーネントのマークアップはクライアントで使用可能ですが、コンポーネントは表示されません。したがって、setVisible(true)
またはsetVisible(false)
メソッドをコールすると、JavaからのコールであろうとJavaScriptからのコールであろうと、コンポーネントがブラウザ内で表示または非表示になります(rendered
がtrue
に設定されている場合)。ただし、visible
は単純にDOMのコンテンツを表示および非表示にするため、rendered
を使用した場合と常に同じ視覚的変化を提供するとはかぎりません。
パフォーマンスに関するヒント
JavaScriptなど、サーバーへのラウンドトリップなしに可視性を切り替える必要がある場合にのみvisible
属性をfalse
に設定します。非表示のコンポーネントも、検証を含むコンポーネント・ライフサイクルを経過します。
クライアントでのみ可視性を切り替える必要がない場合は、かわりにrendered
属性をfalse
に設定します。コンポーネントをレンダリングしない(表示しないのではなく)場合、コンポーネントはクライアント側表現を持たず、コンポーネント・ライフサイクルを経過しないため、サーバーのパフォーマンスとクライアントのレスポンス時間が向上します。
次の例に、2つのoutputText
コンポーネントを示します。一度にその一方のみがレンダリングされます。最初のoutputText
コンポーネントは、値がinputText
コンポーネントに入力されなかった場合にレンダリングされます。2番目のoutputText
コンポーネントは、値が入力されたときにレンダリングされます。
<af:panelGroupLayout layout="horizontal"> <af:inputText label="Input some text" id="input" value="#{myBean.inputValue}"/> <af:button text="Enter"/> </af:panelGroupLayout> <af:panelGroupLayout layout="horizontal"> <af:outputLabel value="You entered:"/> <af:outputText value="No text entered" id="output1" rendered="#{myBean.inputValue==null}"/> <af:outputText value="#{myBean.inputValue}" rendered="#{myBean.inputValue !=null}"/> </af:panelGroupLayout>
コンポーネントがクライアントでレンダリングされる場合、visible
プロパティを使用してコンポーネントをページに表示したり、非表示にできます。
次の例に、前述の例と同じ機能を示しますが、この例ではvisible
属性を使用して、どのコンポーネントが表示されるのか決定します(rendered
属性はデフォルトでtrue
に設定されるため、明示的に設定する必要はありません)。
<af:panelGroupLayout layout="horizontal"> <af:inputText label="Input some text" id="input" value="#{myBean.inputValue}"/> <af:button text="Enter"/> </af:panelGroupLayout> <af:panelGroupLayout layout="horizontal"> <af:outputLabel value="You entered:"/> <af:outputText value="No text entered" id="output1" visible="#{myBean.inputValue==null}"/> <af:outputText value="#{myBean.inputValue}" visible="#{myBean.inputValue !=null}"/> </af:panelGroupLayout>
ただし、visible
属性のかわりにrendered
属性を使用することは、サーバー側でのパフォーマンスを向上させるため、次のJavaScriptコードで示すように、JavaScriptで可視性を処理することもできます。
function showText() { var output1 = AdfUIComponent.findComponent("output1") var output2 = AdfUIComponent.findComponent("output2") var input = AdfUIComponent.findComponent("input") if (input.getValue() == "") { output1.setVisible(true); } else { output2.setVisible(true) } }
コンポーネントのvisible
属性を切り替えられる条件付きJavaScript関数を作成できます。
始める前に:
コンポーネントの表示方法に関する知識が役立つ場合があります。詳細は、「レンダリングおよび可視性の理解」を参照してください。
可視性を設定する手順:
outputText
コンポーネントの可視性を有効にし、そうでない場合は別のoutputText
コンポーネントの可視性を有効にするスクリプトを示します。true
に設定します。これによって、JavaScriptで使用されるクライアント・コンポーネントが作成されます。visible
属性をfalse
に設定します。次の例に、JavaScriptを使用した可視性の切替えに使用されるページ・コード全体を示します。
例4-1 可視性の設定のJavaScript
<f:view> <af:resource> function showText() { var output1 = AdfUIComponent.findComponent("output1") var output2 = AdfUIComponent.findComponent("output2") var input = AdfUIComponent.findComponent("input") if (input.value == "") { output1.setVisible(true); } else { output2.setVisible(true) } } </af:resource> <af:document> <af:form> <af:panelGroupLayout layout="horizontal"> <af:inputText label="Input some text" id="input" value="#{myBean.inputValue}" clientComponent="true" immediate="true"/> <af:button text="Enter" clientComponent="true"> <af:clientListener method="showText" type="action"/> </af:button> </af:panelGroupLayout> <af:panelGroupLayout layout="horizontal"> <af:outputLabel value="You entered:" clientComponent="false"/> <af:outputText value="No text entered" id="output1" visible="false" clientComponent="true"/> <af:outputText value="#{myBean.inputValue}" id="output2" visible="false" clientComponent="true"/> </af:panelGroupLayout> </af:form> </af:document> </f:view>
コンポーネントの親のvisible
属性がfalse
に設定されている場合、visible
属性がtrue
に設定されている子コンポーネントにisVisible
関数が実行されると、子が表示されていなくてもtrue
が返されます。たとえば、outputText
コンポーネントを子として含むpanelGroupLayout
コンポーネントがあり、panelGroupLayout
コンポーネントのvisible
属性はfalse
に設定され、outputText
コンポーネントのvisible
属性はデフォルトのまま(true
)とします。クライアントでは、panelGroupLayout
コンポーネントもoutputText
コンポーネントも表示されませんが、isVisible
関数がoutputText
コンポーネントに実行されると、true
が返されます。
このため、フレームワークにはisShowing()
関数が用意されています。この関数は、コンポーネントのvisible
属性がfalse
に設定されている場合、またはコンポーネントの親のvisible
がfalse
に設定されている場合、false
を返します。
イベントのソースではないクライアント・コンポーネントを検索する必要がある場合、AdfUIComponent.findComponent(expr)
メソッドを使用できます。このメソッドはJSFのUIComponent.findComponent()
メソッドと似ており、指定した検索式に合致するIDを使用してUIComponent
オブジェクトを検索して返します。AdfUIComponent.findComponent(expr)
メソッドは、サーバーではなくクライアントでのみ機能します。
次の例に、コンポーネントのIDを使用してoutputText
コンポーネントを検索するsayHello
関数を示します。
function sayHello(actionEvent) { var buttonComponent=actionEvent.getSource(); //Find the client component for the "greeting" af:outputText var greetingComponent=buttonComponent.findComponent("greeting"); //Set the value for the outputText component greetingComponent.setValue("Hello World") }
ADF Facesには、AdfPage.PAGE.findComponentByAbsoluteId(absolute expr)
メソッドもあります。このメソッドは、IDの文字列をハードコーディングする場合に使用します。クライアントIDがコンポーネントから取得される場合は、AdfUIComponent.findComponent(expr)
を使用します。
注意:
似たような名前のAdfPage.PAGE.findComponent(clientId)
というメソッドもありますが、この関数は、リリース間で変わる可能性のある実装固有の識別子を使用し、ページ作成者は使用しません。
見つける必要があるコンポーネントがネーミング・コンテナ(pageTemplate
、subform
、table
およびtree
など)であるコンポーネントにある場合は、AdfPage.PAGE.findComponentByAbsoluteId(absolute expr)
メソッドを使用するかわりに、AdfUIComponent.findComponent(expr)
メソッドを使用します。絶対式も相対式も使用できます。
ヒント:
コンポーネントがネーミング・コンテナかどうかは、コンポーネント・タグのドキュメントを確認して判断できます。タグのドキュメントには、コンポーネントがネーミング・コンテナかどうかが示されています。
絶対式は次のように作成されます。
":" + (namingContainersToJumpUp * ":") + some ending portion of the clientIdOfComponentToFind
たとえば、myTemplate
テンプレートを使用するページのID r1
のリージョンに含まれるID pc1
のパネル・コレクション・コンポーネント内にあるID t1
の表を検索するには、次の文字列を使用できます。
:myTemplate:r1:pc1:t1
または、両方のコンポーネント(検索を実行するコンポーネントと検索されるコンポーネント)が同じNamingContainer
コンポーネントを階層のどこかで共有する場合は、相対パスを使用して、検索を実行するコンポーネントとの相対で検索を実行できます。相対パスには、たとえば、次のように複数のNamingContainer.SEPARATOR_CHAR
文字を先頭に含めることができます。
":" + clientIdOfComponentToFind
前述の例で、検索元のコンポーネントが表と同じリージョンにある場合、次を使用できます。
::somePanelCollection:someTable
ヒント:
ネーミング・コンテナをフォルダと考え、clientId
をファイル・パスと考えます。フォルダとファイルの場合、連続する2つのピリオドとスラッシュ (../)
を使用して階層の上位のフォルダに移動します。これは、複数のコロン(:
)文字がfindComponent()
式で果たす役割と同じです。先頭の単一のコロン(:
)は、ファイル・パスが、ファイル構造のルートからの絶対パスであることを意味します。式の先頭に複数のコロン(:
)文字がある場合、最初のコロンは無視され、他のコロンは考慮されます。コロン(:
)文字当たり1セットのピリオドとスラッシュ(../
)が考慮されます。
AdfPage.findComponentByAbsoluteId()
メソッドを使用する場合、パスは常に絶対パスであるため、先頭のコロンは不要です。
絶対パスを使用するか相対パスを使用するかを決める際、次の点に注意します。
検索対象のコンポーネントが常に同じネーミング・コンテナ内あることがわかっている場合は、絶対パスを使用します。
検索元のコンポーネントと検索対象のコンポーネントの相対的位置が常に同じであることがわかっている場合は、相対パスを使用します。
クライアントにはgetChildren()
関数もgetFacet()
関数もありません。かわりに、すべての子コンポーネントとファセット(つまり、すべての子孫)にアクセスするAdfUIComponent.visitChildren()
関数が用意されています。ADF Facesはスパース・コンポーネント・ツリー(クライアント・コンポーネントが必要に応じて作成されるツリー)であるため、getParent()
メソッドがクライアントで返すコンポーネントは、サーバー上の実際の親ではない場合があります(先祖である可能性はあります)。同様に、クライアント上に直接の子として出現するコンポーネントは、任意の子孫である可能性があります。詳細は、Oracle ADF Faces Java APIリファレンスを参照してください。
JavaScriptを多用したフレームワークに共通する問題は、大規模なJavaScriptコード・ベースをクライアントに送信する最良の方法を決定することです。極端な方法は、すべてのコードを1つのJavaScriptライブラリにバンドリングすることで、この方法ではダウンロード時間が長くなります。逆の極端な方法はJavaScriptコードを多数の小さなJavaScriptライブラリに分割することで、この方法ではラウンドトリップ数が大きくなります。どちらの方法でも初期ページをロードするためのユーザーの待ち時間が不必要に長くなります。
この問題を軽減するために、ADF FacesではJavaScriptコードがパーティションに集約されます。JavaScriptライブラリ・パーティションには、コンポーネントのコードまたは通常一緒に使用される機能、あるいはその両方が含まれています。ADF Facesではデフォルトで、ダウンロード・サイズ合計とラウンドトリップ数合計のバランスを取ることを意図したパーティション化が行われます。
ADF Facesのライブラリ・パーティション化戦略の利点の1つは、構成可能であることです。アプリケーションによって使用するコンポーネントおよび機能が異なるため、ADF Facesのデフォルトのパーティション化がすべてのアプリケーションに最適であるとはかぎりません。このため、ADF FacesのJavaScriptライブラリのパーティション化はアプリケーションごとにカスタマイズ可能になっています。このようなパーティション化により、アプリケーション開発者はJavaScriptライブラリのフットプリントをアプリケーションの要件に合せて調整できます。
ADF Facesは、そのコンポーネントのJavaScriptファイルをJavaScript機能にグループ化します。JavaScript機能は、その機能を記述する論理識別子に関連付けられたJavaScriptファイルのコレクションです。たとえば、panelStretchLayout
クライアント・コンポーネントは、次の2つのJavaScriptファイルで構成されます。
oracle/adf/view/js/component/rich/layout/ AdfRichPanelStretchLayout.js
oracle/adfinternal/view/js/laf/dhtml/rich/ AdfDhtmlPanelStretchLayoutPeer.js
これらの2つのファイルは、AdfRichPanelStretchLayout
機能にグループ化されます。
JavaScriptの機能は、さらにJavaScriptパーティションにグループ分けされます。JavaScriptパーティションを使用すると、ダウンロード・サイズとラウンドトリップの数に影響を与える目的で、JavaScriptの機能を、より大きなまとまりに分けることができます。たとえば、panelStretchLayout
コンポーネントはpanelSplitter
コンポーネントとともに使用されることが多いため、この2つのコンポーネントの機能は、その子をストレッチできるADF Facesレイアウト・コンポーネントと一緒にストレッチ・パーティションにまとめられます。実行時、ページをロードすると、フレームワークはページで使用されるコンポーネントを決定し、そこから、必要な機能を決定します(機能名は、コンポーネントのコンストラクタ名と同じ)。これらの機能が含まれるパーティションのみがダウンロードされます。
機能およびパーティションは、構成ファイルを使用して定義されます。ADF Facesには、デフォルトの機能およびパーティション構成ファイルが含まれています。独自の実装を作成することでデフォルトのパーティションを上書きできます。
デフォルトでJavaScriptのパーティション化は有効にされています。アプリケーションでJavaScriptのパーティション化を使用するかどうかは、web.xml
ファイルのコンテキスト・パラメータによって決定されます。詳細は、「JavaScriptのパーティション化」を参照してください。
adf-js-partitions.xml
ファイルを作成してから機能にエントリを追加することでJavaScriptパーティションを作成できます。
注意:
ADF Facesには、デフォルトのadf-js-partitions.xml
ファイルがあります(「adf-js-partitions.xmlファイル」を参照)。パーティションの構成を変更する場合は、独自の完全なadf-js-partitions.xml
ファイルを作成する必要があります。実行時にフレームワークは、そのファイルのWEB-INF
ディレクトリを検索します。見つからない場合は、デフォルトのパーティション・ファイルがロードされます。
始める前に:
JavaScriptのパーティション化の動作に関する知識が役立つ場合があります。詳細は、「JavaScriptライブラリのパーティション化」を参照してください。
JavaScriptパーティションを作成する手順:
ADF Facesによって、アプリケーションの初期化時にライブラリのパーティション構成ファイルがロードされます。まず、ADF Facesによって、META-INF
ディレクトリのすべてのadf-js-features.xml
ファイルが検索され、見つかったすべてのファイルがロードされます(ADF Facesのデフォルトの機能構成ファイルを含む)。
パーティション構成ファイルについては、ADF FacesはWEB-INF
ディレクトリでadf-js-partitions.xml
という名前の1つのファイルを検索します。このファイルが見つからない場合は、ADF Facesのデフォルトのパーティション構成が使用されます。
レンダリング・トラバース中、ADF Facesはページによって必要とされるJavaScript機能に関する情報を収集します。トラバース終了時に(レンダリングされた)ページ・コンテンツによって必要とされるJavaScript機能の包括的なセットが認識されます。必要とされるJavaScript機能のセットが認識されると、ADF Facesはパーティション構成ファイルを使用して、この機能セットを、必要とされるパーティションのセットにマップします。必要とされるパーティションのセットを使用して、これらのパーティションへのHTML <script>
参照は、HTMLドキュメントの末尾直前でレンダリングされます。