| Oracle® Fusion Middleware Oracle Application Development Framework Webユーザー・インタフェース開発者ガイド 11gリリース1(11.1.1.9.0) B52029-12 |
|
![]() 前 |
![]() 次 |
この章では、ADF Facesクライアント側アーキテクチャの主な機能について説明します。
この章では、次の項目について説明します。
ADF Facesリッチ・クライアント・フレームワーク(RCF)には、AJAXタイプの機能をWebアプリケーションに作成するために必要な機能が多く用意されており、すべてフレームワークに組み込まれています。RCFの基本は、クライアント側コンポーネント・モデルにあまり移入されないことです。クライアント・コンポーネントは、必要な場合にのみ存在しますが、これは、clientListenerハンドラが登録されている場合、あるいはページ開発者がクライアント側のコンポーネントと対話する必要があり、クライアント・コンポーネントが使用できるように構成してある場合のいずれかです。
クライアント・コンポーネントが存在する主な理由は、フレームワークおよび開発者に対するAPI規定を提供することです。クライアント側コンポーネントは、イベント処理がサポートされる簡単なプロパティ・コンテナとみなすことができます。クライアント・コンポーネントは、状態を格納し、APIを提供するためにのみ存在するため、Document Object Model (DOM)と直接対話しません。DOMとのすべての対話は、ピアと呼ばれる媒介を使用して行われます。フレームワークの内部動作の大部分は、意識する必要がありません。JDeveloperとADF Facesを組み合せて使用すると、アーキテクチャ上の機能の多くを宣言して使用でき、コードを作成する必要がありません。
たとえば、RCFでは、各サーバー側コンポーネントに対応するクライアント・コンポーネントをすべて作成するわけではないため、クライアント版のコンポーネント・インスタンスが必要な場合もあります。3.4項「クライアント側コンポーネントのインスタンス化」に、これを宣言的に行う方法が説明されています。JDeveloperのプロパティ・インスペクタを使用して、3.9項「レンダリングおよび可視性の理解」で説明されている、コンポーネントをレンダリングするかどうか、あるいは非表示にするかどうかを決めるプロパティを設定します。
その他の機能では、ADF Faces JavaScript APIの使用が必要な場合があります。たとえば、3.5項「ページでのクライアント・コンポーネントの検索」では、APIを使用して特定のクライアント側コンポーネントを探す方法を説明し、3.7項「クライアントでのコンポーネント・プロパティへのアクセス」では、特定のプロパティにアクセスする方法を説明しています。
次のRCF機能はより複雑です。このため、個別の章が使用されています。
ライフサイクルへのADF Facesの追加機能: ADF FacesフレームワークでJSFライフサイクルを拡張し、クライアント側の値のライフサイクルなどの追加機能を提供します。詳細は、第4章「ADF FacesでのJSFライフサイクルの使用」を参照してください。
イベント処理: ADF FacesはJSFイベント処理手法に準拠しています。また、RCFにはAJAXベースのリッチ・ポストバック(部分ページ・レンダリングと呼ばれる)とクライアント側イベント・モデルが用意されています。詳細は、第5章「イベントの処理」を参照してください。
変換および検証: ADF Faces入力コンポーネントには、ユーザー入力の変換および検証の両方の機能が組み込まれています。独自のカスタム・コンバータとバリデータを作成することもできます。詳細は、第6章「入力の検証および変換」を参照してください。
部分ページ・レンダリング: 部分ページ・レンダリング(PPR)を使用すると、ページ内の小さい領域を、ページ全体を再描画せずにリフレッシュできます。ADF Facesコンポーネントの多くにPPR機能が組み込まれています。また、あるコンポーネントに対するアクションによって別のコンポーネントが再レンダリングされるようPPRを宣言的に構成することもできます。詳細は、第7章「部分ページ・コンテンツの再レンダリング」を参照してください。
ジオメトリ管理: ADF Facesには、レイアウト・コンポーネントが多く用意されており、この多くでコンテンツを使用可能な領域いっぱいに自動的に拡大することでジオメトリ管理をサポートします。詳細は、第8章「Webページ上のコンテンツの編成」を参照してください。
メッセージおよびヘルプ: RCFには、入力コンポーネントのツールチップ、メッセージおよびヘルプを表示する機能と、アプリケーションのグローバル・メッセージを表示する機能が用意されています。ヘルプ・フレームワークによって、アプリケーション全体で再利用可能なメッセージを作成できます。Javaクラス、マネージドBean、XLIFFファイルまたは標準プロパティ・ファイルを使用してヘルプ・プロバイダを作成したり、HTMLベースの外部ヘルプ・システムにリンクできます。詳細は、第17章「ヒント、メッセージおよびヘルプの表示」を参照してください。
階層型メニュー・モデル: ADF Facesには、階層式のページをナビゲートするためのタブやブレッドクラムなどのアイテムをレンダリングするナビゲーション・コンポーネントが用意されています。RCFで、メタデータ・ファイルとともに使用し、各ページに適切な数の階層レベルを生成するためのすべての情報と、各レベルに属するナビゲーション・アイテムを含むXMLベースのメニュー・モデルが提供されます。詳細は、第18章「ナビゲーション・コンポーネントの使用」を参照してください。
再利用可能なコンポーネント: RCFには、アプリケーションの複数のページで使用できる再利用可能な3つのビルディング・ブロックが用意されています(ページ・フラグメントでは、ページの一部(住所入力フォームなど)を作成できます。ページ・テンプレートでは、アプリケーション全体で一貫したルック・アンド・フィールが得られ、更新可能で、テンプレートを使用するすべてのページに変更が自動的に伝播されます。宣言コンポーネントは、開発者が再利用できるコンポジット・コンポーネントで、アプリケーション全体で動作に矛盾がないようにします)。詳細は、第19章「フラグメント、ページ・テンプレート、コンポーネントの作成および再利用」を参照してください。
スキンの適用: RCFでは、ADF Facesコンポーネントで外観の変更に使用するスキンを作成することで、独自のルック・アンド・フィールを作成できます。詳細は、第20章「スタイルおよびスキンを使用した外観のカスタマイズ」を参照してください。
国際化およびローカライズ: 異なるロケールを使用するようJSFページまたはアプリケーションを構成し、ユーザーのブラウザの言語設定に基づいて適切な言語を表示できます。詳細は、第21章「ページの国際化およびローカライズ」を参照してください。
アクセシビリティ: ADF Facesコンポーネントには、ユーザー・エージェント(スクリーン・リーダー、拡大鏡などのビジュアル・メディア以外にレンダリングするWebブラウザ)に対するアクセシビリティ・サポートが組み込まれています。アクセシビリティ・サポートには、ユーザーがキーボードのみを使用してコンポーネントやリンクにアクセスできるアクセス・キー、アクセシブルHTMLマークアップを使用したアクセシブルなイメージ、表、フレーム、フォーム、エラー・メッセージおよびポップアップ・ダイアログの作成手順を提供する監査ルールも含まれます。詳細は、第22章「アクセス可能なADF Facesページの開発」を参照してください。
ユーザー駆動型パーソナライズ: 多くのADF Facesコンポーネント(panelSplitterなど)で、ユーザーは実行時にコンポーネントの表示を変更できます。デフォルトでは、これらの変更はページ・リクエストの間有効です。ただし、変更がユーザーのセッションの間永続するようアプリケーションを構成できます。詳細は、第34章「JSFページでのユーザー・カスタマイズの許可」を参照してください。
ドラッグ・アンド・ドロップ機能: RCFで、ユーザーは、ある場所から別の場所へデータを移動(切り取って貼付け)、コピー(コピーして貼付け)およびリンク(コピーしてリンクとして貼付け)できます。ドロップが完了すると、ドロップの受入れ先コンポーネントで部分ページ・レンダリングを使用して再レンダリングが行われます。詳細は、第35章「ドラッグ・アンド・ドロップ機能の追加」を参照してください。
この章の以降の項では、クライアント側フレームワークの使用に焦点を当てます。
従来のJSFアプリケーションでは、クライアントでイベントを処理する場合、DOMレベルのイベントをリスニングする必要があります。ただし、これらの実装は移植可能な形式で配信されません。ADF Facesのクライアント側イベント・モデルはJSFイベント・モデルと似ていますが、クライアントで実装されます。クライアント側イベント・モデルはDOMの要約で、コンポーネントレベルのイベント・モデルとライフサイクルが提供され、サーバーから独立して実行されます。このため、ボタンのclickイベントをリスニングする必要がありません。かわりに、キーまたはマウスのイベントで発生するAdfActionEventイベントをリスニングできます。
クライアントによって送信されるイベントはすべてAdfBaseEventクラスのサブクラスです。各クライアント・イベントには、イベントをトリガーしたコンポーネントであるソースがあります。イベントには、イベントをリスニングするリスナーを判断するタイプ(actionまたはdialogなど)もあります。クライアント・リスナーは、af:clientListenerタグを使用してコンポーネントに登録します。
たとえば、クリックされると「Hello World」アラートを表示するボタンがあるとします。例3-1に示すように、まず、イベント・ハンドラを起動するリスナーをボタンに登録します。
例3-1 クライアント・リスナーの登録
<af:commandButton text="Say Hello"> <af:clientListener method="sayHello" type="action"/> </af:commandButton>
|
ヒント: ボタンにクライアント・リスナーが登録されているため、フレームワークで、クライアント版のコンポーネントが自動的に作成されます。 |
次に、例3-2に示すようなハンドラをJavaScript関数に作成します。
クライアント版のコンポーネントがあるため、ボタンがクリックされると、AdfActionクライアント・イベントが起動されます。AdfActionイベントをリスニングするようclientListenerタグが構成されているため、sayHello関数が実行されます。クライアント側イベントの詳細は、5.3項「ADF Facesクライアント・イベントに対するJavaScriptの使用」を参照してください。
インラインJavaScriptをページに直接追加するか、JavaScriptライブラリをページにインポートできます。ライブラリをインポートする場合は、ページ・コンテンツ・サイズが削減され、ライブラリをページ間で共有でき、ブラウザによってキャッシュできます。可能な場合には必ずJavaScriptライブラリをインポートする必要があります。インラインJavaScriptは、ページ固有の小さなスクリプトが必要な場合にのみ使用します。
|
パフォーマンスのヒント: インラインJavaScriptは、レスポンス・ペイロード・サイズを増加させる可能性があり、ブラウザにまったくキャッシュされないため、ブラウザのレンダリングをブロックすることがあります。インラインJavaScriptを使用するかわりに、すべてのスクリプトをJavaScriptライブラリに配置することを検討してください。インラインJavaScriptを使用する必要がある場合、必要とするページにのみこれを含めます。ただし、ほとんどのページで同じJavaScriptコードが使用されている場合、スクリプトを含めることを検討するか、テンプレートにライブラリをインポートしてください。 JavaScriptコード・ライブラリが非常に大きくなった場合、ライブラリを内容に応じて分割し、(テンプレートに配置しないで)ページで必要な部分のみを含めることに注意してください。この方法で、ブラウザ・キャッシュが使用され、ページのHTMLコンテンツが小さくなるため、パフォーマンスが向上します。 |
インラインのJavaScriptは、JSFアプリケーションと同様に作成および使用します。JavaScriptがページに含まれる場合、clientListenerタグを使用して起動します。
インラインのJavaScriptを使用する手順:
例3-3に太字で示すコードを追加して、MyFaces Trinidadタグ・ライブラリをページのルート要素に追加します。
ページにJavaScriptを作成します。
たとえば、例3-2に示すsayHello関数は、例3-4のようにJSFページに含めることができます。
|
注意: ページまたはテンプレートにf:verbatimタグを使用してJavaScriptを指定しないでください。 |
「構造」ウィンドウで、JavaScriptを呼び出すコンポーネントを右クリックし、「componentの中に挿入」→「ADF Faces」→「クライアント・リスナー」の順に選択します。
「クライアント・リスナーの挿入」ダイアログで「メソッド」フィールドにJavaScript関数の名前を入力します。タイプ・フィールドで、関数を起動するイベント・タイプを選択します。
af:resourceタグを使用して、ページからJavaScriptライブラリにアクセスします。このタグは、documentタグのmetaContainerファセットの内部に含める必要があります。
ページからJavaScriptライブラリにアクセスする手順:
documentタグの下に、例3-5に太字で示すコードを追加し、JavaScriptライブラリが含まれるディレクトリへの相対パスに/mySourceDirectoryを置き換えます。
「構造」ウィンドウで、JavaScriptを呼び出すコンポーネントを右クリックし、「componentの中に挿入」→「ADF Faces」→「クライアント・リスナー」の順に選択します。
クライアント・リスナーの挿入ダイアログで「メソッド」フィールドに関数の完全修飾名を入力します。たとえば、sayHello関数がMyScriptsライブラリにある場合、MyScripts.sayHelloと入力します。タイプ・フィールドで、関数を起動するイベント・タイプを選択します。
JavaScriptでクライアントにアクセスする必要がある場合、リスナーのコンテキストで行われ、イベントのソース・コンポーネントにアクセスする必要がある場合がしばしばあります。getSource()メソッドを使用してクライアント・コンポーネントを取得します。例3-6に、名前を表示するためにソース・クライアント・コンポーネントにアクセスするsayHello関数を示します。
例3-6 クライアント・イベント・ソースへのアクセス
function sayHello(actionEvent)
{
var component=actionEvent.getSource();
//Get the ID for the component
var id=component.getId
alert("Hello from "+id);
}
クライアント・イベント・ソースへのアクセスの詳細は、5.3項「ADF Facesクライアント・イベントに対するJavaScriptの使用」を参照してください。クライアント側プロパティへのアクセスの詳細は、3.7項「クライアントでのコンポーネント・プロパティへのアクセス」を参照してください。実行時にクライアント・イベントを処理する方法の詳細は、5.3.6項「実行時の処理: クライアント側イベントの機能の仕方」を参照してください。
RCFでは、どのコンポーネントが、対応するクライアント側コンポーネント・インスタンスをデフォルトで持つかについて保証されません。通常、clientListenerハンドラを登録して、クライアント側コンポーネントと対話します。登録されているclientListenerハンドラがコンポーネントにある場合、コンポーネントはクライアント側表現を自動的に持ちます。クライアントの別のコンポーネントにアクセスする必要がある場合、clientComponent属性をtrueに設定して、そのコンポーネントがクライアントで使用できるよう明示的に構成します。
|
パフォーマンスのヒント: クライアントのコンポーネントとプログラムで対話する場合にのみclientComponentをtrueに設定します。 |
clientComponent属性をtrueに設定すると、コンポーネントのAdfUIComponentクラスのインスタンスがフレームワークで作成されます。このクラスでは、クライアント側で操作できるAPIが提供され、基本的なプロパティ・アクセッサ・メソッド(getProperty()、setProperty()など)、イベント・リスナーの登録およびイベント配信関連のAPIも提供されます。フレームワークには、プロパティ固有のアクセッサ・メソッド(getText()、setText()など)を公開するレンダラ固有のサブクラス(AdfRichOutputTextなど)も用意されています。これらのアクセッサ・メソッドは、AdfUIComponentクラスのgetProperty()およびsetProperty()メソッドをラップするのみのラッパーで、コーディングが簡便になるよう用意されています。
たとえば、sayHello関数から値(表示するテキスト)を取得するoutputTextコンポーネントがページにあるとします。この関数は、値を設定するためにoutputTextコンポーネントにアクセスできる必要があります。これが機能するには、クライアント側バージョンのoutputTextコンポーネントがある必要があります。例3-7に、JSFページ・コードを示します。outputTextコンポーネントにid値があり、clientComponent属性がtrueに設定されていることに注意してください。また、値はJavaScriptで設定されるため、例には値がないことにも注意してください。
例3-7 コンポーネントの追加
<af:commandButton text="Say Hello"> <af:clientListener method="sayHello" type="action"/> </af:commandButton> <af:outputText id="greeting" value="" clientComponent="true">
outputTextにクライアント側表現があるため、JavaScriptでこれを見つけて使用できます。
イベントのソースではないクライアント・コンポーネントを検索する必要がある場合(3.3.3項「クライアント・イベント・ソースへのアクセスに関する必知事項」で説明)、その絶対IDを使用するか、または別のコンポーネントからの相対でコンポーネントを検索できます。
絶対IDは、完全修飾のJSFクライアントIDです。コンポーネントがネーミング・コンテナ内にある場合、そのIDには、接頭辞としてそのコンポーネントが含まれるすべてのNamingContainerコンポーネントのIDが先行するNamingContainer.SEPARATOR_CHAR文字とともに付き、たとえば、次のようになります。
":" + (namingContainersToJumpUp * ":") + some ending portion of the clientIdOfComponentToFind
たとえば、myTemplateテンプレートを使用するページの、IDがr1のリージョンに含まれる、IDがpc1のパネル・コレクション・コンポーネント内にある、IDがt1の表の絶対IDは、次のようになります。
myTemplate:r1:pc1:t1
両方のコンポーネント(検索を実行するコンポーネントと検索されるコンポーネント)が同じNamingContainerコンポーネントを共有する場合は、コンポーネントのIDのみを使用する必要があります。たとえば、IDがt1の表を検索しようとし、その表が検索を実行する入力テキスト・コンポーネントと同じネーミング・コンテナに存在する場合は、単に次のように使用します。
t1
検索するコンポーネントが階層内の異なるネーミング・コンテナに存在する場合は、相対パスを使用して、検索を実行するコンポーネントとの相対で検索を実行できます。現在のネーミング・コンテナから親のネーミング・コンテナに「ステップ・アウト」するには、区切り文字(:)を使用します。たとえば、親ネーミング・コンテナ内のコンポーネントを検索するには、次のように使用します。
":" + clientIdOfComponentToFind
前述の例で、検索元のコンポーネントが表と同じリージョンにある場合、次を使用できます。
::somePanelCollection:someTable
|
ヒント: コンポーネントがネーミング・コンテナかどうかは、コンポーネント・タグのドキュメントを確認して判断できます。タグのドキュメントには、コンポーネントがネーミング・コンテナかどうかが示されています。 |
絶対IDを使用するか相対パスを使用するかを決めるときは、次の点に注意します。
検索対象のコンポーネントが常に同じネーミング・コンテナ内あることがわかっている場合は、絶対IDを使用します。
検索元のコンポーネントと検索対象のコンポーネントの相対的位置が常に同じであることがわかっている場合は、相対パスを使用します。
AdfPage.PAGEオブジェクトは、コンポーネントIDまたはコンポーネント・ロケータ(子をスタンプ設定する表などのコレクション・コンポーネントに使用される)のいずれかによって、ページ上のすべてのコンポーネントにアクセスできます。
次のAdfPage.PAGEの関数は、絶対IDでページ上のコンポーネントを検索するために使用できます。
findComponentByAbsoluteId: ほとんどのコンポーネントで使用します。
絶対コンポーネントIDは、コンポーネントID、およびドキュメント・ルートとページ上のターゲット・コンポーネントとの間に配置されているネーミング・コンテナのIDから作成されます。
(namingContainersToJumpUp * ":") + some ending portion of the clientIdOfComponentToFind
たとえば、myTemplateテンプレートを使用するページの、IDがr1のリージョンに含まれる、IDがpc1のパネル・コレクション・コンポーネント内にある、IDがt1の表の絶対IDは、次のようになります。
myTemplate:r1:pc1:t1
この関数は、scopeIdまたは検索式を渡すサーバー・メソッドのUIViewRoot.findComponent()と同等のクライアント側の関数です。クライアントとサーバーのコンポーネント検索の違いは、clientComponent属性をtrueに設定するかまたはclientListenerを使用し、コードで具体的にクライアント・コンポーネントを作成しないかぎり、クライアント・コンポーネントが存在しないことがある、という点です。フレームワークがそれ自身の目的のため、たまたまクライアント・コンポーネントを作成することがあるとしても、そのクライアント・コンポーネントは完全なものではない可能性があります。したがって、常に独自のクライアント・コンポーネントを作成する必要があります。
さらに、サーバーでは、tableまたはiteratorなどの反復コンポーネントは、それらが中に含む各コンポーネントのシングル・インスタンスをスタンプ・アウトします。クライアントでは、各反復ごとに1つのインスタンスが作成されます。したがって、コンポーネントの繰り返しのたびにIDが変わります。このため、反復およびコレクション・コンポーネント内のコンポーネントの検索にfindComponentByAbsoluteIDは使用できません。
findComponentByAbsoluteLocator: 表などの反復的な(つまり、コレクション)コンポーネント、または自動化されたテストで使用します。
ロケータIDには、スタンプ設定されたコンポーネント・インスタンスを検索する、絶対的なスタンプ設定の索引が含まれます。たとえば、そのIDがit1である表のセル内のinputTextコンポーネントの絶対ロケータは次のようになります。
pc1:t1[absolute stamped index].it1
findComponent: コンポーネントの絶対IDが変わる可能性がある場合、またはページに重複したコンポーネントIDが存在する可能性がある場合、たとえば、検索されるコンポーネントがリージョン内にあるときなどに使用します。
AdfPage.PAGE.findComponent関数は、コンポーネントを、その絶対IDではなくクライアントIDで検索します。これは、コンポーネントのインスタンスを取得する手軽な方法です。クライアントIDは、HTMLページ出力の中に実行時に生成され、設計時に定義されるコンポーネントIDとは異なります。クライアントIDは一定ではなく、ページの再実行でも変化するため、JavaScriptでクライアントIDの値をハードコードしないでください。そのかわりに、クライアントIDは、たとえば、JavaScriptのイベントを発生させるコンポーネントのaf:clientAttributeタグを使用して、実行時にサーバー側のコンポーネントから読み取って動的にJavaScriptに追加します。
findComponentの使用方法の詳細は、3.5.3項「絶対IDがわからない場合のクライアント・コンポーネントの検索方法」を参照してください。
|
ヒント: 参照する必要のあるコンポーネントで、clientID属性をtrueに設定することを忘れないでください。 |
次の関数は、別のコンポーネントとの相対でのコンポーネントの検索に使用できます。
findComponent: ほとんどのコンポーネントのために使用します。
findComponentメソッドは、指定されたコンポーネント・インスタンスからコンポーネントの検索を開始します。検索するコンポーネントIDは、最も近い祖先となるネーミング・コンテナの範囲内で一意である必要があります。
たとえば、nc1がnc2の親となるネーミング・コンテナの階層を持つとします。nc1に属するボタン(b1)があり、nc2にあるコンポーネント(c2)を検索する必要があるとします。この場合に使用する相対的な検索IDは、nc2:c2になります。
検索ID内の先行するコロンにより、現在のネーミング・コンテナからその親コンテナに移動して検索できます。たとえば、コンポーネントの検索をnc2内のボタンb1から開始し、2つのコロンを接頭辞としてコンポーネントの検索IDに付けた場合(::c2)、親ネーミング・コンテナnc1にあるコンポーネントc2が検索されます。追加される個々のコロンにより階層内の1つ上の親コンテナに移動するため、:::c2はnc1の親ネーミング・コンテナ内でコンポーネントを検索することになり、それがドキュメント・ルートである可能性もあります。先行する単一のコロンは、コンポーネントをドキュメント・ルートから直接検索します。
|
ヒント: ネーミング・コンテナをフォルダと考え、clientIdをファイル・パスと考えます。フォルダとファイルに関して、2つの連続ピリオドとスラッシュ(../)を使用して、階層内の上にフォルダに移動します。これは、複数のコロン(:)文字がfindComponent()式で果たす役割と同じです。先頭の単一のコロン(:)は、ファイル・パスが、ファイル構造のルートからの絶対パスであることを意味します。式の先頭に複数のコロン(:)文字がある場合、最初のコロンは無視され、他のコロンは考慮されます。コロン(:)文字当たり1セットのピリオドとスラッシュ(../)が考慮されます。 |
findComponentByAbsoluteLocator: 表のようなスタンプ設定された(つまり、コレクション)コンポーネントのために使用します。
この関数は、絶対IDでの検索時にAdfPage.PAGEオブジェクトで使用されるものと完全に同じです。これは、検索で提供される絶対ロケータIDを必要とします。この関数はコンポーネントからの相対で検索を開始しますが、内部的にはAdfPage.PAGE.findComponentByAbsoluteLocatorを呼び出します。
クライアントにはgetChildren()関数もgetFacet()関数もありません。かわりに、すべての子コンポーネントとファセット(つまり、すべての子孫)にアクセスするAdfUIComponent.visitChildren()関数が用意されています。詳細は、ADF Faces JavaScriptのドキュメントを参照してください。
実行時までコンポーネントの絶対IDがわからないこともあります。マネージドBeanから動的にIDを検索できます。Beanで、サーバー側のgetClientIDを使用してIDにアクセスします。clientAttributeタグを使用してクライアント・コンポーネントのIDを設定し、クライアント側でAdfPage.PAGE.findComponentByAbsoluteIdを使用してJavaScriptからIDにアクセスします。
始める前に:
コンポーネントIDを追跡するために使用される、ページのバッキングBeanを作成する必要があります。参照されるコンポーネントをバッキングBeanにバインドします。例3-8に、commandButtonのバインドに使用されるコードを示します。
例3-8 コンポーネントIDを保持するバッキングBeanのコード
private ComponentReference clientButton;
public void setClientButton(RichCommandButton clientButton){
this.clientButton = ComponentReference.newUIComponentReference(clientButton);
}
public RichCommandButton getClientButton(){
if (clientButton != null){
return (RichCommandButton)clientButton.getComponent();
}
else{
return null;
}
}
絶対IDのわからないクライアント・コンポーネントにアクセスするには:
JavaScriptから参照されるコンポーネントでは、clientComponentをtrueに設定します。
同じバッキングBeanで、コンポーネントのJava参照でgetClientId()関数を呼び出す新規の関数を作成します。この関数では、クライアントIDおよび任意のテンプレート、リージョンまたは他のネーミング・コンテナで定義されたパスを含む、完全なIDが戻されます。例3-9に、commandButtonのための関数を示します。
そのイベントがJavascriptを起動するコンポーネントにclientListenerタグを追加します。また、JavaScript関数がそのIDを使用可能にするためのclientAttributeタグを追加します。clientAttributeタグにより、通常はアクセスできないクライアント・コンポーネントに属性を追加できます(詳細は、3.8項「クライアント側コンポーネントに対するボーナス属性の使用」を参照)。例3-10に、「b1」ボタンの参照に必要な第2のボタンを示します。
AdfPage.PAGE.findComponentByAbsoluteId(absolute expr)メソッドを使用してクライアントIDにアクセスするJavaScript関数を作成します。
ADF Facesでは、イベントに関して現在のコンテキスト・ページ情報を返すJavaScript APIが用意されています。AdfPage.prototype.getViewId()関数では、現在表示されているビューの識別子を返します。このIDは、ページ全体のレンダリングまたは部分ページ・ナビゲーションのいずれかが発生すると設定されます。AdfPage.prototype.getComponentsByType(componentType)関数は、指定したコンポーネント・タイプに一致するコンポーネント・インスタンスの配列を返します。
たとえば、アプリケーションにタブ付きのページが含まれており、各タブが複数のリージョンで構成されているとします。各リージョンには、ネストされた他のリージョンも含めることができます。APIを使用すると、例3-12に示すように、ページ全体のviewIdと、ページで現在レンダリングされている各リージョンに表示されているフラグメントのviewIdsの組合せとなる文字列の識別子を返すことができます。
クライアント側のリージョン・コンポーネントのviewID propertyを取得するには、web.xmlファイルのパラメータを設定して、ユーザー・アクティビティ監視機能を有効にする必要があります。次に、現在のページを構成するviewIdsの文字列表現をビルドするJavaScriptコードを作成します。
コンテキスト識別子を決定する手順:
web.xmlファイルをダブルクリックします。
ソース・エディタで、oracle.adf.view.faces.context.ENABLE_ADF_EXECUTION_CONTEXT_PROVIDERをtrueに設定します。
このパラメータでは、ExecutionContextProviderサービス・プロバイダが有効であることがADF Facesに通知されます。このサービスでは、クライアント開始のリクエストのユーザー・アクティビティ情報を監視して集約します。
oracle.adf.view.rich.automation.ENABLEDをtrueに設定します。
このパラメータにより、すべてのコンポーネントでコンポーネントIDが設定されます。詳細は、A.2.3.10項「テストの自動化」を参照してください。
JavaScriptを作成し、コンテキスト識別子をビルドします(JavaScriptの追加の詳細は、3.3項「JavaScriptのページへの追加」を参照)。
例3-12に、リージョンの現在のビューIDを取得するのに使用するJavaScriptを示します。
例3-12 JavaScriptによるviewIdsの取得
/**
* Returns a String identifier comprising the page viewId and viewIds
* of the fragments displayed in each of the displayed regions
*/
TestLibrary.getCurrentPageInfo = function()
{
var pageIdentifier = null;
var page = AdfPage.PAGE;
if (page)
{
// get the viewId of the page
var viewId = page.getViewId();
// get all region components currently displayed on the page
var regionComponents = page.getComponentsByType("oracle.adf.RichRegion");
var regionViewIds = new Array();
for (var index = 0; index < regionComponents.length; index++)
{
var regionComp = regionComponents[index]);
if (regionComp)
{
regionViewIds.push(regionComp.getProperty("viewId"));
}
}
// construct page identifier
if (viewId != null && regionViewIds.length > 0)
contextId = viewId.concat(regionViewIds.toString());
}
return contextId;
}
コンポーネントの各組込みプロパティには、簡易アクセッサ・メソッドがコンポーネント・クラスに用意されています。たとえば、クライアント・コンポーネントでgetValue()メソッドをコールし、サーバーで使用されたのと同じ値を受け取ることができます。
|
注意: ADF Faces内のブール・プロパティを含むすべてのクライアント・プロパティでgetXyz関数命名規則を使用します。ブール・プロパティのisXyz命名規則は使用されません。 |
定数もクラス・オブジェクトのプロパティ名に使用できます。たとえば、styleClassを使用するかわりにAdfRichDialog.STYLE_CLASS定数を使用できます。
|
注意: 文字列のコーディングは各起動時にオブジェクトの割当てが必要なため、JavaScriptでは定数を参照する方が効率的です。 |
コンポーネントのプロパティが変更されると、最終的な結果として、コンポーネントのDOMが新しい状態を反映するように更新されます。場合によっては、サーバーへのラウンドトリップは発生しません。このプロセスにおけるコンポーネントのロールはかなり限られています。新しいプロパティ値を格納してから、変更をピアに通知するだけです。ピアには、新しいコンポーネント状態を反映するようにDOMを更新するロジックが含まれます。
|
注意: すべてのプロパティの変更が、クライアント側のピアを使用して処理されるわけではありません。一部のプロパティ変更はサーバーに伝播され、PPRを使用してコンポーネントがレンダリングされます。 |
1.2.2項「ADF Facesのアーキテクチャ上の機能」での説明のとおり、大半のプロパティ値はクライアントで設定されると、サーバーと自動的に同期されます(クライアントにまったく送信されない複雑なJavaオブジェクトもあります)。ただし、これとは異なる動作をするセキュア・プロパティと分離プロパティの2種類のプロパティがあります。
セキュア・プロパティは、クライアントにまったく設定できないプロパティです。たとえば、悪意のあるクライアントでJavaScriptを使用してcommandLinkコンポーネントのimmediateフラグがtrueに設定されるとします。この変更がサーバーに伝播された結果、サーバー側の検証が省略され、セキュリティ・ホールとなる可能性があります(immediateプロパティの使用の詳細は、4.2項「immediate属性の使用」を参照してください)。つまり、immediateプロパティはセキュア・プロパティです。
JavaScriptから他のセキュア・プロパティの設定を試みると失敗します。詳細は、3.7.2項「無効なプロパティを保護しない方法」を参照してください。表3-1に、クライアント・コンポーネントのセキュアなプロパティを示します。
表3-1 セキュア・クライアント・プロパティ
| コンポーネント | セキュア・プロパティ |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ADF Facesでは、disabledプロパティを非保護にできるように構成できます。この機能は、JavaScriptを使用してボタンを有効および無効にする必要がある場合に役立ちます。unsecureプロパティをtrueに設定すると、disabledプロパティ(およびdisabledプロパティのみ)が保護されなくなります。
分離プロパティは、クライアントで設定できるがサーバーに伝播することはできないプロパティです。これらのプロパティには、サーバーのライフサイクルに依存しないクライアント上のライフサイクルがあります。たとえば、クライアント・フォーム入力コンポーネント(AdfRichInputTextなど)には、JavaのEditableValueHolderコンポーネントのようにsubmittedValueプロパティがあります。ただし、このプロパティの設定はサーバーに直接影響しません。この場合、標準のフォーム送信手法によって、送信された値のサーバーでの更新を処理します。
プロパティは、分離とセキュアの両方にすることができます。実際には、このようなプロパティはクライアント上で分離プロパティのように動作します。これらはクライアントで設定できますが、サーバーには送信されません。ただし、サーバーではセキュア・プロパティとして動作するため、クライアントがこれらを設定しようとすると拒否されます。
RCFには、AdfUIComponentsetProperty()関数をコールするためのsetXYZ簡易関数が用意されています。setProperty()関数は次の引数をとります。
プロパティ名(必須)
新しい値(必須)
unsecuredプロパティを使用して、disabledプロパティを非保護に設定します。disabledプロパティを非保護にする必要のあるコンポーネントのコードに、このプロパティとdisabledの値を手動で追加する必要があります。たとえば、disabledプロパティを非保護にする必要のあるボタンのコードは次のようになります。
<af:commandButton text="commandButton 1" id="cb1" unsecure="disabled"/>
unsecure属性をdisabledに設定すると、悪意のあるJavaScriptがdisabled属性を不注意で変更する可能性があります。たとえば、支出承認ページがあり、そのページで特定のマネージャが$200未満の請求書のみ承認できるようにするとします。そのため、現在のユーザーが請求書の承認を許可されていないかぎり、承認ボタンを無効にする必要があります。
unsecured属性disabledに設定していない場合、承認ボタンは、現在のユーザーが支出を承認できるかどうかを判断するロジックがあるサーバーとのラウンドトリップが発生するまで無効なままになります。ただし、ページに支出がロードされたときにはボタンを正しく表示する必要があるため、unsecure属性をdisabledに設定します。これで、クライアントでJavaScriptを使用して、ボタンを無効にするかどうかを判断できます。ただし、この場合は、任意のJavaScript(制御できない悪意のあるJavaScriptを含む)が同じことを行う可能性があります。
このような問題を回避するには、サーバーへのラウンドトリップが実行されるのと同様に、同じロジックをアプリケーションで実行する必要があります。支出レポート承認画面で、金額が$200未満であることをチェックするJavaScriptがあっても、承認ボタンのアクションではサーバー上のロジックを実行する必要があります。サーバーにロジックを追加すると、無効な属性を変更してはならない場合に、それが変更されません。
同様に、アプリケーションを実行時に変更できるようにし、ユーザーがunsecureまたはdisabled属性、あるいはその両方を編集できるようにする場合は、サーバーとのラウンドトリップが発生した場合と同じロジックがアプリケーションによって実行されるようにする必要があります。
組込みプロパティ以外の情報をクライアントに送信する必要がある場合もあります。これは、ボーナス属性を使用して行えます。ボーナス属性は、clientAttributeタグを使用してコンポーネントに追加できる追加属性です。パフォーマンス上の理由から、クライアントに送信されるボーナス属性は、clientAttributeで指定されたもののみです。
clientAttributeタグでは、サーバー側コンポーネントの属性マップに追加される名前と値のペアを指定します。サーバー側属性マップの移入に加えて、clientAttributeタグを使用すると、ボーナス属性がクライアントに送信され、AdfUIComponent.getProperty("bonusAttributeName")メソッドを使用してアクセスできます。
RCFで、属性値のクライアントへのマーシャリングが行われます。マーシャリング・レイヤーで、文字列、ブール、数値、日付、配列、マップなど、各オブジェクト型のマーシャリングがサポートされます。マーシャリングの詳細は、5.4.3項「データのマーシャリングとアンマーシャリングに関する必知事項」を参照してください。
|
パフォーマンスのヒント: マーシャリングによる過度のオーバーヘッドを防ぐため、クライアント側ボーナス属性を多用しないでください。 |
|
注意: clientAttributeタグは、(アプリケーションで定義される)ボーナス属性にのみ使用します。クライアントで標準のコンポーネント属性にアクセスする必要がある場合、clientAttributeタグを使用するのではなく、clientComponent属性をtrueに設定します。詳細は、3.4項「クライアント側コンポーネントのインスタンス化」を参照してください。 |
コンポーネント・パレットを使用して、ボーナス属性をコンポーネントに追加できます。
ボーナス属性を作成する手順:
「構造」ウィンドウで、ボーナス属性を追加するコンポーネントを選択します。
コンポーネント・パレットで、「操作」パネルから「クライアント属性」をコンポーネントに子としてドラッグ・アンド・ドロップします。
プロパティ・インスペクタで「名前」と「値」属性を設定します。
クライアント側ボーナス属性は、サーバーからクライアントへ自動的に配信されますが、その逆は行われません。つまり、クライアントでのボーナス属性の変更または設定は、サーバーに反映されません。既知(ボーナス以外)の属性のみ、クライアントからサーバーへ同期がとられます。アプリケーションで定義されたデータをサーバーに送り返す必要がある場合、カスタム・イベントを作成します。詳細は、5.4項「クライアントからサーバーへのカスタム・イベントの送信」を参照してください。
すべてのADF Faces表示コンポーネントには、コンポーネントをページに表示してユーザーに見えるようにするかどうかに関係するrenderedとvisibleの2つの属性があります。
rendered属性のセマンティクは非常に厳密です。renderedがfalseに設定されている場合、サーバーへのラウンドトリップなしにコンポーネントをクライアントで表示することはできません。ページのコンテンツの動的な表示/非表示をサポートするために、RCFにはvisible属性が追加されています。falseに設定されている場合、コンポーネントのマークアップはクライアントで使用可能ですが、コンポーネントは表示されません。したがって、setVisible(true)またはsetVisible(false)メソッドをコールすると、JavaからのコールであろうとJavaScriptからのコールであろうと、コンポーネントがブラウザ内で表示または非表示になります(renderedがtrueに設定されている場合)。
|
パフォーマンスのヒント: JavaScriptなど、サーバーへのラウンドトリップなしに可視性を切り替える必要がある場合にのみvisible属性をfalseに設定します。非表示のコンポーネントも、検証を含むコンポーネント・ライフサイクルを経過します。
クライアントでのみ可視性を切り替える必要がない場合は、かわりに |
例3-13に、2つのoutputTextコンポーネントを示します。一度にその一方のみがレンダリングされます。最初のoutputTextコンポーネントは、値がinputTextコンポーネントに入力されなかった場合にレンダリングされます。2番目のoutputTextコンポーネントは、値が入力されたときにレンダリングされます。
例3-13 レンダリングされる/レンダリングされないコンポーネント
<af:panelGroupLayout layout="horizontal">
<af:inputText label="Input some text" id="input"
value="#{myBean.inputValue}"/>
<af:commandButton 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プロパティを使用してコンポーネントをページに表示したり、非表示にできます。
例3-14に、例3-13に示したものと同じ機能を実現する方法を示しますが、この例では、visible属性を使用してどのコンポーネントが表示されるのかを決定します(rendered属性はデフォルトでtrueに設定されるため、明示的に設定する必要はありません)。
例3-14 表示可能/表示不可能なコンポーネント
<af:panelGroupLayout layout="horizontal">
<af:inputText label="Input some text" id="input"
value="#{myBean.inputValue}"/>
<af:commandButton 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で可視性を処理することもできます。
例3-15に、コンポーネントの可視性を処理するJavaScriptのページ・コードを示します。
例3-15 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関数を作成できます。
可視性を設定する手順:
可視性を切り替えるJavaScriptを作成します。例3-15に、値がない場合にoutputTextコンポーネントの可視性を有効にし、そうでない場合は別のoutputTextコンポーネントの可視性を有効にするスクリプトを示します。
JavaScript関数に必要なコンポーネントごとに、プロパティ・インスペクタで「拡張」セクションを開き、ClientComponent属性をtrueに設定します。これによって、JavaScriptで使用されるクライアント・コンポーネントが作成されます。
可視性を切り替えるコンポーネントのvisible属性をfalseに設定します。
例3-16に、JavaScriptを使用した可視性の切替えに使用されるページ・コード全体を示します。
例3-16 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:commandButton text="Enter" clientComponent="true">
<af:clientListener method="showText" type="action"/>
</af:commandButton>
</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が返されます。
このため、RCFにはisShowing()関数が用意されています。この関数は、コンポーネントのvisible属性がfalseに設定されている場合、またはコンポーネントの親のvisibleがfalseに設定されている場合、falseを返します。