Oracle ADF UIX開発者ガイド | ![]() 目次 |
![]() 前へ |
![]() 次へ |
table
は、Oracle ADF UIX Componentsテクノロジで使用可能な最も強力で複雑なユーザー・インタフェース・コンポーネントです。 表形式のデータを表示または更新するWebアプリケーションでは、table
を1つ以上使用する必要があります。 このトピックでは、table
コンポーネントの構成方法をすべて調べます。
ここでは、次の項目について説明します。
Table
の必要性Table
の概念Table
でのデータ・バインディング
TableData
Table
の必要性 Table
で使用可能なオプションを見ると、どんな問題でも解決できるように考えられますが、そうではありません。 Table
は、2次元の形式で記述されたデータを表示(場合によっては更新)するために用意されています。 Table
を使用すると効率的な場合をまとめると、次のようになります。
これらに当てはまらない場合、Table
は使用に適した部品ではありません。 次の経験則は、Table
の使用が適切な場合を示していますが、すべての状況に適用されるわけではありません。
一方、Table
を使用できない状況もあります。
TableLayoutBean
またはその他のレイアウト機能を使用します。
前述の条件を踏まえてもTableの使用が適切であると思われる場合のために、次のセクションから作成方法を説明します。
Table
の概念Table
の外観を設計する際には、比較されるデータ項目が行として存在し、これらの項目に対する個々の内容または処理が列として存在することに常に留意してください。 項目の性質が似ていることがすでに確認されているため、それらの項目に対する個々の内容数も一致する必要があります。 したがって、Table
の列はすべての行に対して同じになります。 ただし、表示するデータ項目数および行数が異なる場合があります。 たとえば、Webアプリケーションのショッピング・カートを表すために使用するTable
では、最初は項目が1つもありませんが、ショッピングの後には複数の項目が存在します。
データ項目の行と列が交差する各部分をセルと呼びます。 したがって、表は多くのセルの2次元表示になります。各データ項目に対して1行のセルがあり、セルの列は、多くのデータ項目間で比較される同一の内容または処理を表します。
Table
では、ユーザーがデータを連想しやすいラベルが必要です。 これには、列ヘッダーおよび行ヘッダーを使用します。列ヘッダーは列の最上部にある水平方向のラベルで、ショッピング・カート内の品目の価格など、列に含まれる内容または処理を表し、左端に垂直方向に表示される行ヘッダーは、各データ項目の前で使用して、たとえば実際に発注する注文を明確化します。 データの概要を表示すると便利な場合は、表の下部で列フッターを使用します。 一貫性を保つために、列ヘッダーおよび列フッターの項目にもセルがあるものとします。
Table
では、拡張した表示および追加情報の表示に他の書式オプションを使用できますが、これらについてはこのトピック全体を通して説明していきます。 それでは、表を作成しましょう。
先に進む前に、最初のUIX XML例を見てみましょう。 これは最も基本的な表です。
<table xmlns="http://xmlns.oracle.com/uix/ui"/>
この例は設計が十分ではなく、実際には何も表示しません。 これは、UIX Componentsのネームスペースにtable要素を表示することを宣言したものです。 実際にはまだ表を構成していないため、空白のように見えます。 何かを表示するには、データ項目および列を追加する必要があります。これにはスタンプを使用します。
スタンプとは何でしょうか。 スタンプとは、(任意の)UINode
で、さらに特定のページで複数回レンダリングされるものを意味します。 機密書類にスタンプで印を付けるように使用されるたびに同じマークを生成するため、スタンプと呼ばれます。 スタンプには、繰り返す必要のあるボタン、テキスト・フィールドまたはその他の任意の部品を指定できます。 Table
では各列にスタンプが1つ存在し、このスタンプが表の列のセルすべてに対して下方向に繰り返され、レンダリングされます。 表に列のスタンプを追加するには、列の索引付けされた子を表に追加してから、索引付けされた子を列に追加します。 列の索引付けされた子はそれぞれ新規の列スタンプになります。列の索引付けされた最初の子のUINodeが一番左の列スタンプになり、後続の列の子は直前のスタンプの右に追加されます。
表の列にデータをレンダリングするためにスタンプの概念を使用するのはなぜでしょうか。 表は、類似するデータ項目の比較に使用されることに注意してください。 各列はデータ項目の1つの内容を表すため、列のセルの外観を類似させることには意味があります。 各セルのスタンプを類似させるが同一にはしない場合については、このトピックで後述します。
最初の列スタンプをtableの例に追加します。 これを行うには、column
の索引付けされた子をtable
に追加して、索引付けされた子をcolumn
に追加します。 column
要素自体は何もレンダリングしませんが、column
の索引付けされた子がその表列の各セルに設定されます。
<table ... >
<contents>
<!-- the first column stamp is a text node -->
<column>
<contents>
<text text="SampleText"/>
</contents>
</column>
</contents>
</table>
tableにスタンプを追加しましたが、まだ特に何も表示されません。 これは、次の基本のルールによるものです。
DataObject
に対して1回レンダリングされる。 このルールは、DataObject
がない場合、スタンプはレンダリングされないことを意味します。そこで、データをいくつか追加します。
必要なDataObject
は、最初に調べる属性であるtableData
により提供されます。この属性は、表の行を表します。 前述したように、表の行数はその存続期間中に変化することがあります(ユーザーがショッピング・カートの品を追加または削除した場合など)。 可変長リストを伴うすべてのUIX Componentsの概念と同様に、このtableData
属性では、タイプとしてDataObjectList
が含まれています。 DataObjectList
は、長さを返すことと、各DataObjectへの索引によるアクセスを可能にすることという2つの処理のみを実行することに注意してください。 tableData
では、DataObjectList
の長さで表示される行数が決まり、リスト内の各DataObject
で対応する行のデータを提供します。
例にサンプル・データを配置します。 これは単なるデモであるため、UIX XMLのインライン・データ機能を使用します。 ほとんどの実際のアプリケーションでは、より複雑な動的ソースを介してデータを公開します。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
<!-- all the row DataObjects used by our table for tableData -->
<demoRowData/>
<demoRowData/>
<demoRowData/>
</inline>
</data>
</provider>
<contents>
<table tableData="${uix.data.demoTableData.demoRowData}">
<contents>
<column>
<contents>
<!-- the first column stamp is a text node -->
<text text="SampleText"/>
</contents>
</column>
</contents>
</table>
</contents>
</dataScope>
デモのコードにいくつかの項目を追加しました。
<dataScope>
要素に配置し、表にデータソースを指定しました。 表自体は<dataScope>
のコンテンツ部に移動され、データは<provider>
セクションに配置されています。
DataObjectList
にマージします。マージされたDataObjectList
は要素3つ分のサイズで、各要素はDataObjectList
の索引付けされた3つのDataObject
の1つを表します。
tableData
という新規の属性があります。この属性は、行の表示に必要なDataObjectList
をtableに提供します。 具体的には、providerセクションに配置したdemoRowDataというDataObjectList
が、行データソースに選択されます。
この例を実行すると、縦列に3つのセルがあり、各列に同じサンプル・テキストのある表が表示されます。 これは、1つの列スタンプ(text要素)があり、tableData
のリストに3つのDataObject
があるためです。前述のルールのとおり、テキスト・スタンプはリスト内のすべてのDataObject
に対して1回レンダリングされます。 このため、テキストが3回表示されます。
少し変更を加えるとどうなるでしょうか。 まず、行リストに4つ目のDataObject
を追加します。 各スタンプはすべてのDataObject
に対して1回レンダリングされるという基本のルールのとおり、表に4行がレンダリングされます。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
<!-- all the row DataObjects used by our table for tableData -->
<demoRowData/>
<demoRowData/>
<demoRowData/>
<demoRowData/>
</inline>
</data>
</provider>
<contents>
...
</contents>
</dataScope>
次に、2番目の列スタンプを追加して、表の大きさを変えてみます。今回のスタンプはボタンです。 ルールのとおり、既存のテキスト・スタンプと新規のボタンの両方がそれぞれ4回レンダリングされます。
<dataScope ... >
<provider>
...
</provider>
<contents>
<table tableData="${uix.data.demoTableData.demoRowData}">
<contents>
<column>
<contents>
<!-- the first column stamp, a text node -->
<text text="SampleText"/>
</contents>
</column>
<column>
<contents>
<!-- the second column stamp, a button -->
<button text="Push Me" destination="http://www.oracle.com"/>
</contents>
</column>
</contents>
</table>
</contents>
</dataScope>
この2つの概念を拡張し、表に任意の数の行を配置したり、列のタイプおよび数を変更することができます。 試してみてください。
Table
でのデータ・バインディングスタンプは、すべての行に同じデータを表示する表には適していますが、実際にはこのような表はありません。 有用な例を作成するには、データ・バインディングが必要です。
データ・バインディングは、レンダリングごとにページのコンテンツを変更するため、UIX Components全体で使用されることに注意してください。 ここではデータ・バインディングをさらに一歩進め、行ごとに表のコンテンツを変更します。 UINode
でスタンプされる列は行ごとに変更されないため、データ・バインドを使用して、特定のセルで実際にスタンプされる内容を変更します。 これにより、列の各セルにスタンプされるコンテンツのタイプは似ているが(列の各セルがテキスト・フィールドであるなど)、コンテンツ自体は異なる(各テキスト・フィールドのテキストが行ごとに異なるなど)というような、より有用な目的でスタンプを使用できます。
これは、UIX Componentsの現行のDataObject
の指定という概念で機能します。 他のUIX Componentsのデータ・バインドと同様に、ノードの属性は、特定のDataObject
ではなく現行指定されたDataObject
にバインドできます。 これにより、問合せ時にどのDataObject
が現行であるかに基づいて、生成される結果が変わります。 表では、表の各行をレンダリングする際に現行のDataObject
を変更することにより、この概念を可能にします。 このため、列スタンプの属性が現行のDataObject
の同じキーにバインドされている場合、その属性の実際の値は表のすべての行で変わります。
表のtableData DataObjectList
の各要素は、対応する行の現行のDataObject
として使用されるため、DataObject
を使用してレンダリングする行数を決定することは適切な処理です。 説明のため、前述の例を変更します。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
<!-- all the row DataObjects used by our table for tableData -->
<demoRowData someText="First row"/>
<demoRowData someText="Second row"/>
<demoRowData someText="Third row"/>
<demoRowData someText="Fourth row"/>
</inline>
</data>
</provider>
<contents>
<table tableData="${uix.data.demoTableData.demoRowData}">
<contents>
<column>
<contents>
<!-- the first column stamp, a text node -->
<text text="${uix.current.someText}"/>
</contents>
</column>
<column>
<contents>
<!-- the second column stamp, a button -->
<button text="${uix.current.someText}" destination="http://www.oracle.com"/>
<contents>
</column>
</contents>
</table>
</contents>
</dataScope>
これで、行ごとにコンテンツの異なる表ができました。 変更箇所は次のとおりです。
DataObject
それぞれにデータを1つ追加しました。このデータは、DataObject
がキーsomeTextで問合せされた場合に返されるテキスト文字列です。 結果は行ごとに変えました。
DataObject
を問い合せた結果として返されるようにしました。 属性値として"${uix.data.aNamedDataObject.someText}"ではなく"${uix.current.someText}"を使用したため、この問合せは、特定のDataObject
ではなく、現行指定されたDataObject
に対して行われます。 したがって、各行にスタンプするテキストは、表データから取得されます。
明確にするため、テキスト・スタンプとボタン・スタンプで別のデータを使用するよう例を微調整します。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
<!-- all the row DataObjects used by our table for tableData -->
<demoRowData firstColumnText="First row" secondColumnText="Button #1"/>
<demoRowData firstColumnText="Second row" secondColumnText="Button #2"/>
<demoRowData firstColumnText="Third row" secondColumnText="Button #3"/>
<demoRowData firstColumnText="Fourth row" secondColumnText="Button #4"/>
</inline>
</data>
</provider>
<contents>
<table tableData="${uix.data.demoTableData.demoRowData}">
<contents>
<column>
<contents>
<!-- the first column stamp, a text node -->
<text text="${uix.current.firstColumnText}"/>
</contents>
</column>
<column>
<contents>
<!-- the second column stamp, a button -->
<button text="${uix.current.secondColumnText}" destination="http://www.oracle.com"/>
</contents>
</column>
</contents>
</table>
</contents>
</dataScope>
各行のDataObject
に、1つではなく、firstColumnTextおよびsecondColumnTextという2つのテキスト値を与えました。 続いて、テキスト列のスタンプをfirstColumnText
にバインドし、ボタンをsecondColumnText
にバインドしました。 その結果、2つの列が異なるテキストを持つようになります。
この例は単純ですが、考え方は非常に有用です。 任意のUINode
を列スタンプとして使用することが可能で、そのスタンプの任意の属性を現行指定されているDataObject
にバインドできることに留意してください。 これにより、表のセルのコンテンツを非常に柔軟に制御できます。
この時点で、いくつかの疑問が発生します。 スタンプを使用し、データをわざわざ分離している理由は何でしょうか。 開発者が、セルごとにコンテンツを、またはコンテンツとデータをまとめて指定できないのでしょうか。
表のモデル(またはデータ)をビュー(または外観)から分離することには相応の理由があります。 分離することにより、開発者は、列スタンプを変更して表の外観を指定し、後で任意のデータソースをプラグインしてこれらのスタンプに具体的なデータを提供できます。 各セルに入れるデータを厳密に指定すると、同じセットのUIX Componentsのノードをすべてのページ・ビューに使用することはできません。 レンダリングごとに表の行数を変更したり、各セルのデータを変更することもできません。 表の構造を一度指定し、その構造をすべてのページ・レンダリングに再利用することは、UIX Componentsフレームワーク全体のコア機能の1つです。
TableData
tableData
属性はDataObjectList
タイプであるため、いくつかの方法で設定できます。 変更されない静的データは、次の例で示すようにUIX XMLコードにインラインで定義できます。
<table ...>
<tableData>
<demoRowData firstColumnText="First row" secondColumnText="Button #1"/>
<demoRowData firstColumnText="Second row" secondColumnText="Button #2"/>
<demoRowData firstColumnText="Third row" secondColumnText="Button #3"/>
<demoRowData firstColumnText="Fourth row" secondColumnText="Button #4"/>
</tableData>
...
</table>
UIX XMLパーサーは、tableData
がDataObjectList
タイプであることを認識し、tableData
要素の子をDataObjectList
に解析します。 行キー(この場合はdemoRowData
)は任意に設定できますが、4つの行すべてが同じ行キーを持ち、同じDataObjectList
にグループ化される必要があります。 ちなみに、ここにはデータ・バインドはありません。tableData
属性は、新規に作成されるDataObjectList
に設定されます。
このアプローチには、表データを複数の表で共有できないという問題があります。 共有は、データ・バインドおよび(前述の)次のような構成を使用して行うことができます。
<dataScope ... >
<provider>
<data name="demoTableData">
<inline>
<demoRowData firstColumnText="First row" secondColumnText="Button #1"/>
<demoRowData firstColumnText="Second row" secondColumnText="Button #2"/>
<demoRowData firstColumnText="Third row" secondColumnText="Button #3"/>
<demoRowData firstColumnText="Fourth row" secondColumnText="Button #4"/>
</inline>
</data>
</provider>
<contents>
<table name="table1" tableData="${uix.data.demoTableData.demoRowData}">
...
</table>
<table name="table2" tableData="${uix.data.demoTableData.demoRowData}">
...
</table>
</contents>
</dataScope>
この例ではinline
データ・プロバイダが、DataObjectList
にバインドされる、単一のキーであるdemoRowData
のあるDataObject
を作成します。 外部のDataObject
は不要です。次のセクションでは、Javaで表データを作成し、外部のDataObject
は作成しません。
データ・バインドにより、JavaでDataObjectList
を作成し、それらを表にデータとして提供できます。 次に、簡単な例を示します。
<dataScope ... >
<provider>
<data name="demoTableData">
<method class="test.MyTable" method="getTableData" />
</data>
</provider>
<contents>
<table tableData="${uix.data.demoTableData}">
...
</table>
</contents>
</dataScope>
前述したtableData
バインドでの変更点に注意してください。 DataObject
の内部でDataObjectList
をラップしないためです。 表データを作成するJavaコードを次に示します。 (UIX XMLおよびJavaでのデータ・バインドの詳細は、どちらも「ADF UIXでのデータ・バインディング」のトピックを参照してください)。
package test;
public class MyTable
{
public static DataObject getTableData(RenderingContext context,
String namespace,
String name)
{
DataObject[] data = new DataObject[4];
data[0] = new MyDataObject("First Row", "Button #1");
data[1] = new MyDataObject("Second Row", "Button #2");
data[2] = new MyDataObject("Third Row", "Button #3");
data[3] = new MyDataObject("Fourth Row", "Button #4");
// convert the array into a DataObjectList
return new ArrayDataSet(data);
}
private static final class MyDataObject implements DataObject
{
public MyDataObject(String column1, String column2)
{
_col1 = column1;
_col2 = column2;
}
public Object selectValue(RenderingContext context, Object key)
{
if ("firstColumnText".equals(key))
return _col1;
else if ("secondColumnText".equals(key))
return _col2;
return null;
}
private final String _col1, _col2;
}
}
この例では、2つのキーを認識するDataObject
の独自の実装を作成します。 次にデータのインスタンスの配列を作成し、oracle.cabo.ui.data.ArrayDataSet
クラスを使用してそこからDataObjectList
を作成します。 ArrayDataSet
はDataObject
およびDataObjectList
の両方を実装するため、DataProvider
内で表データを返すために使用できることに注意してください。
これまでは静的データを作成してきました。 ここでは、動的データを作成するJavaコードを実行します。次の例では、ディレクトリ・リストのある表データを作成します。
package test;
public class MyTable
{
public static DataObject getDirectoryData(RenderingContext context,
String namespace,
String name)
{
// Make sure this directory exists on your file system
return new DirDataObjectList(new File("/home/user/"));
}
private static final class DirDataObjectList
implements DataObjectList, DataObject
{
public DirDataObjectList(File dir)
{
_files = dir.listFiles();
}
public int getLength()
{
return _files.length;
}
public DataObject getItem(int index)
{
// in a more prudent implementation, we would be caching these
// DataObjects, rather than creating new ones each time.
return new FileDataObject(_files[index]);
}
public Object selectValue(RenderingContext context, Object key)
{
// we don't support any properties on this DataObject, since this is
// primarily a list of DataObjects.
return null;
}
private final File[] _files;
}
private static final class FileDataObject implements DataObject
{
public FileDataObject(File file)
{
_file = file;
}
/**
* This DataObject recognizes two keys: name which gives the
* file name, and length which gives the file length.
*/
public Object selectValue(RenderingContext context, Object key)
{
if ("name".equals(key))
return _file.getName();
else if ("length".equals(key))
return new Long(_file.length());
return null;
}
private final File _file;
}
}
対応するUIX XMLコードは次のようになります。
<dataScope ... >
<provider>
<data name="demoTableData">
<method class="test.MyTable" method="getDirectoryData" />
</data>
</provider>
<contents>
<table tableData="${uix.data.demoTableData}">
<contents>
<column>
<contents>
<text text="${uix.current.name}"/>
</contents>
</column>
<column>
<contents>
<text text="${uix.current.length}"/>
</contents>
</column>
</contents>
...
</table>
</contents>
</dataScope>
Javaでの表データの作成に役立つもう1つのクラスは、Java Vector
(およびJDK1.2以上のList
)をDataObjectList
に変換するoracle.cabo.ui.data.ListDataObjectList
です。 サーバー側での表データの処理に役立つクラスは他にもあります。これらのクラスについては、後のセクションで説明します。
これまでは、表のデータ部分のみを扱い、データを取り囲む表の周辺に関する項目は扱いませんでした。 ここではまず、各列にラベルを提供する列ヘッダーを調べます。 次に、各行にラベルを提供する行ヘッダーを調べます。
表のcolumn
の索引付けされた子には、UINodeをとる名前の付けられた子columnHeader
があります。 このUINodeは、列のヘッダーです。 次の例でこれを示します。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
<!-- all the row DataObjects used by our table for tableData -->
<demoRowData firstColumnText="First row" secondColumnText="Button #1"/>
<demoRowData firstColumnText="Second row" secondColumnText="Button #2"/>
<demoRowData firstColumnText="Third row" secondColumnText="Button #3"/>
<demoRowData firstColumnText="Fourth row" secondColumnText="Button #4"/>
</inline>
</data>
</provider>
<contents>
<table tableData="${uix.data.demoTableData.demoRowData}">
<contents>
<!-- the first column stamp, a text node -->
<column>
<!-- add a columnHeader named child for the column header -->
<columnHeader>First Header</columnHeader>
<contents>
<text text="${uix.current.firstColumnText}"/>
</contents>
</column>
<!-- the second column stamp, a button -->
<column>
<!-- add a columnHeader named child for the column header -->
<columnHeader>Second Header</columnHeader>
<contents>
<button text="${uix.current.secondColumnText}" destination="http://www.oracle.com"/>
</contents>
</column>
</contents>
</table>
</contents>
</dataScope>
column
の名前の付けられた子columnHeader
があることに注意してください。columnHeader
には列ヘッダー・テキストを指定できます。
これは、多くの表に使用できます。 ただし、一部の表では、ユーザーがなんらかの基準によりデータ項目をソートできるようにし、行が現在ソートされていることを示す必要もあります。 このためUIX Componentsでは、このニーズを満たすSortableHeaderBean
という特定の列ヘッダー・スタンプを提供しています。 このBeanについては、後のセクションで説明します。
一部の表では、個々のデータ行に付属のラベルでラベルを付けることが必要な場合があります。 これを行うために、表のBeanではrowHeaderStamp
を提供しています。 行ヘッダー・スタンプの使用方法を示す次の例について説明します。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
...
<!-- DataObjectList to provide information to the row header stamps -->
<demoRowHeaderData headerText="1"/>
<demoRowHeaderData headerText="2"/>
<demoRowHeaderData headerText="3"/>
</inline>
</data>
</provider>
<contents>
<form ... >
<contents>
<table ...
rowHeaderData="${uix.data.demoTableData.demoRowHeaderData}" >
<contents>
...
</contents>
<!-- row header stamp node -->
<rowHeaderStamp>
<text text="${uix.current.headerText}"/>
</rowHeaderStamp>
</table>
</contents>
</form>
</contents>
</dataScope>
各行ヘッダーには、rowHeaderStamp
という名前の付けられた子とrowHeaderData
表属性を持つ独自の個別のテキストがあります。 ここでは次の3つの作業を行いました。
DataObjectList
を作成しました。このDataObjectList
のサイズは行数と同じで、各DataObject
には、headerTextキーで格納された、行ヘッダーに表示するテキストが含まれています。
rowHeaderData
がデータ・バインド属性から取得されるよう登録しました。この属性の値は、demoTableDataという名前のDataObject
の下にあるdemoRowHeaderDataというDataObjectList
です。 これにより、表で新規のDataObjectList
を使用して、行ヘッダー・スタンプにデータを提供できます。
DataObject
のheaderTextキーから取得されるようバインドしました。 行ヘッダー・スタンプがレンダリングされる際、行ヘッダー・データ内の個々のDataObject
が、各ヘッダーにスタンプがレンダリングされるときの現行のDataObject
になります。 これにより、テキストが行ヘッダーごとに変更されます。 ただし、この例では、行ヘッダー・データの4つのDataObject
を使用していないので注意してください。 これは、データがなくてもヘッダー・スタンプが行をレンダリングしますが、望ましい結果を得られない場合があることを示すためです。
ユーザーが編集可能なコントロールをレンダリングするスタンプを、意図的に表に含める場合があります。 ユーザーが表のセルにデータを入力し、そのデータをサーバーに送信して処理するというものです。
たとえば、表の列のすべてのセルに編集可能なフォームの入力要素をレンダリングし、ユーザーがそれらのセルのデータを入力または変更できるようにする場合を考えます。 通常この処理は、標準のフォーム送信を介して表の外部のページで実行されます。 任意のフォームの入力項目には、name
属性が関連付けられています。この属性は、フォームが送られる際に入力要素の値とともにサーバーに送信されます。 この方法で、サーバーにページの値が通知されます。
前述のように、項目は入力要素であっても表の列スタンプとして使用できます。 しかし、項目が列のセルを垂直方向に複数回スタンプされるとしたら、各セルはどのようにして一意の値としてサーバーに送信されるのでしょうか。 各入力要素が同じname
属性で複数回レンダリングされ、目的のHTMLが得られないのではないでしょうか。
その答えは、表が名前の変換と呼ばれるプロセスを介してこれを修正することにあります。 基本的に、表はスタンプ上のname
属性を特別なものとして扱い、スタンプをセルにレンダリングする前にこの属性を変更します。 たとえば、列スタンプがfoo
という名前の入力コントロールだった場合、表は各セルにレンダリングされる実際のname値を次の形式に変更します。
tableName:foo:rowIndex
tableNameはtableのname
属性の値で置換され、rowIndexはスタンプがレンダリングされる行を示す整数で置換されます。 つまり、myTestTable
という名前の表の第3行にレンダリングされるfoo
という名前の入力要素には、次の名前が与えられます。
myTestTable:foo:3
次の例では、表を変更して、最初の列にテキスト・ノードのかわりにtextInput
要素を配置しました。 このページのHTMLソースを表示すると、表の各行の生成済テキスト・フィールドの名前が前述のように変換されているのがわかります。
<dataScope ... >
<provider>
...
</provider>
<contents>
<form name="testForm">
<contents>
<table name="myTestTable"
... >
<contents>
<column>
<columnHeader>First Header</columnHeader>
<contents>
<textInput text="${uix.current.firstColumnText}" name="foo"/>
</contents>
</column>
<column>
<columnHeader>Second Header</columnHeader>
<contents>
<button text="${uix.current.secondColumnText}" destination="http://www.oracle.com"/>
</contents>
</column>
</contents>
</table>
</contents>
</form>
</contents>
</dataScope>
ナビゲーション・バー・リンクまたはその他のフォーム起動アクションの結果としてこの表を含むフォームが送信されると、サーバーではtextInput
列の値を表す4つの名前と値のペアを受信します。
フォーム・コントロール名 | 初期値 |
---|---|
myTestTable:foo:0 | First row |
myTestTable:foo:1 | Second row |
myTestTable:foo:2 | Third row |
myTestTable:foo:3 | Fourth row |
UIXには、これらの値が送信された後にサーバーで値を取得するためのユーティリティ・クラスも用意されています。 クラスoracle.cabo.data.ServletRequestDataSet
は、ServletRequest
から値を取得するために使用します。 クラスoracle.cabo.servlet.ui.data.PageEventFlattenedDataSet
は、UIXサーブレットのPageEvent
で使用します。 これらのクラスは、表の入力要素すべてによるDataObjectList
を実装します。 このリストの長さは、表の行数と同じです。 このリストの各DataObject
は、表の行に対応します。 入力要素のname
を(各DataObject
で)キーとして使用し、(各行の)その要素の値を取得できます。 次の例では、表の入力要素すべての値を取得して連結するイベント・ハンドラを実装しています。
public static EventResult doSubmitEvent(BajaContext bc, Page page,
PageEvent event)
{
// create a new FlattenedDataSet for the table "myTestTable"
DataSet tableInputs = new PageEventFlattenedDataSet(event,
"myTestTable");
StringBuffer s = new StringBuffer(40);
// this would be the number of rows in the table
int sz = tableInputs.getLength();
for(int i=0; i<sz ;i++)
{
// get the DataObject representing all the input elements on the current
// table row.
DataObject row = tableInputs.getItem(i);
// get the value of the input element named "foo". we can safely use
// null for the RenderingContext here:
Object value = row.selectValue(null, "foo");
s.append(value);
}
EventResult result = new EventResult(page);
result.setProperty("case", "submit");
// store the concatenation of the values of all the "foo" elements on the
// EventResult
result.setProperty("result", s);
return result;
}
この自動的な名前変換が不要な場合があります。たとえば、列の垂直方向にラジオ・ボタン・グループをレンダリングする場合は、(各ラジオ要素が同じグループに属し、相互に排他的となるよう)各ラジオ要素を同じ名前にする必要があります。 現在のところ、これを実行する唯一の方法は、自動的な名前変換をオフにし、各name
属性をデータ・バインディングすることにより名前変換をプライベートに処理することのみです。 自動的な名前変換は、table
のnameTransformed
属性をfalse
(またはJavaではBoolean.FALSE
)に設定してオフにします。
<table ...
nameTransformed="false">
...
</table>
上記の場合でも、PageEventFlattenedDataSet
(またはServletRequestDataSet
)を使用してサーバー上でデータを取得したい場合は、oracle.cabo.ui.data.FlattenedDataSet
を使用して表のすべての入力要素を変換します。 表myTestTable
の5行目の入力要素foo
を変換するには、次のように指定します。
String newName = FlattenedDataSet.getFlattenedName("myTestTable", 5, "foo");
radio
という名前の例にあげたようなラジオ・グループ列を変換するには、次のように指定します。
String newName = FlattenedDataSet.getFlattenedName("myTestTable", "radio");
最後のステップは、すべてを正しく動作させるために必要です。 table
のproxied
属性を、true
に設定する必要があります(この属性については後のセクションで説明します)。 これで、通常の方法でPageEventFlattenedDataSet
を使用して、表の入力要素の値を取得できるようになります。 例にあげたようなラジオ・グループ列の値を取得するには、次のように指定します。
DataSet tableInputs = new PageEventFlattenedDataSet(event,
"myTestTable");
Object radioValue = tableInputs.selectValue(null, "radio");
いくつかの行がある表の作成方法を見てきましたが、アプリケーションのデータ・セットに多くの項目が含まれることがよくあります。 大企業の従業員をリスト表示する表などでは、すべてのレコードを同時に表示できないことは明白です。 このため、レコード・ナビゲーションを使用して、このような表を管理しやすいように分割する必要があります。
わずかなデータを追加することにより、表のBeanで、現行のデータ行がより大きなデータの一部であることを示すナビゲーション領域をレンダリングできます。 表の各行には索引番号が割り当てられ、どの行番号が現在表示され、どの行番号が表示されていないかがユーザーに通知されます。 このナビゲーション領域をレンダリングするには、表に追加属性をいくつか指定する必要があります。
value
: 画面に現在表示されている先頭行の番号です。 指定されない場合、デフォルトは1です。
minValue
: 画面に表示されている行のみでなく、データ・セット全体での先頭行の索引です。 指定されない場合、minValue
は1と想定されます。
maxValue
: 画面に表示されている行のみでなく、データ・セット全体での最終行の索引です。 指定されない場合は、ナビゲーション・バーの外観が変更され、行の総数が不明であることが示されます。
blockSize
: データ・セット当たりに表示される行数で、デフォルトは25です。表は、行の最後のセットの行数が他の行セットよりも少ない場合を自動的に処理します。 たとえば、403レコードのセットで25レコードを一度にナビゲートする場合、(blockSize
が25であっても)最後のセットには3レコードのみが含まれます。
不明な場合は、4つの属性すべてを表に指定する必要はありません。 4つのうち1つのみを指定しても、表にナビゲーション領域がレンダリングされ、明示的な値がなくてもこれらの属性がデフォルトになります。 例として、現在画面に表示されているデータよりも多くのデータがあることを示すナビゲーション・プロパティを追加したデモの表を示します。
<dataScope ... >
<provider>
...
</provider>
<contents>
<table value="5"
maxValue="50"
blockSize="4"
... >
<contents>
...
</contents>
</table>
</contents>
</dataScope>
この例では、行を(合計50行のうちの)5行目から表示し、一度に最大4行を表示するよう表を設定しています。 最小値は指定していませんが、1と想定されます。
ナビゲーション・バーは純粋に表面的なものであることに注意してください。実際にレンダリングされる行数や最初にレンダリングされる行の制御は行いません。 value
属性およびblockSize
属性の値にかかわらず、表は常にtableData
属性のDataObject
と同じ数の行をレンダリングします。
ナビゲーション・リンクを使用すると、表はUIXサーブレットのUIConstants.GOTO_EVENT
というイベント(UIX XMLではgoto
)を生成します(イベントの詳細は、「ADF UIXでのコントローラの使用」のトピックを参照してください)。 このイベントには、次の表で説明する3つのパラメータがあります。
イベント・パラメータ | UIConstant | 説明 |
---|---|---|
source | SOURCE_PARAM |
イベントを生成した表を示します。 値は、tableのname属性です。 |
value | VALUE_PARAM |
ユーザーが現在表示している行セットを示します。 セットの先頭行の索引に設定されます。 |
size | SIZE_PARAM |
現在表示されている行セットのサイズです。 行の最後のセットが表示される場合を除き、これは通常、表のblockSize です。 |
サーバーでは、要求された表の行のみを含む新規のDataObjectList
を作成する必要があります。 つまり、valueパラメータで指定した行から開始し、sizeパラメータと同じ行数のみが含まれます。 これはoracle.cabo.ui.data.PagedDataObjectList
クラスを利用して行います。
public class TableDemo {
public static EventResult doGotoEvent(BajaContext bc, Page page,
PageEvent event)
{
// if this is a "goto" event, then we need to get the "value" parameter to
// figure out what our start index is. If this is not a "goto" event, then
// we want to start at index "1"
String valueParam = ((event!=null) &&
UIConstants.GOTO_EVENT.equals(event.getName()))
? event.getParameter(UIConstants.VALUE_PARAM)
: "1";
// the "value" parameter starts at "1"; however, our data is zero based,
// so adjust the offset
int value = Integer.parseInt(valueParam)-1;
DataObjectList tableData = new PagedDataObjectList(_TABLE_DATA,
_BLOCK_SIZE.intValue(),
value); //start index
// in a more efficient implementation, we would not use DictionaryData;
// instead, we would implement our own DataObject
DictionaryData data = new DictionaryData();
// we need to add one here, since our data is zero based, but the table
// start index must start at 1
data.put("value", new Integer(value+1));
data.put("size", _BLOCK_SIZE);
data.put("maxValue", new Integer(_TABLE_DATA.getLength()));
data.put("current", tableData);
EventResult result = new EventResult(page);
result.setProperty("tableData", data);
return result;
}
// we want to render at most 30 rows on a single page
private static final Integer _BLOCK_SIZE = new Integer(30);
// create some dummy data with 998 rows:
private static final DataObjectList _TABLE_DATA = new DummyData(998);
private static final class DummyData implements DataObjectList
{
public DummyData(int size)
{
_size = size;
}
public int getLength()
{
return _size;
}
public DataObject getItem(final int index)
{
return new DataObject()
{
public Object selectValue(RenderingContext rc, Object key)
{
return "Test Data "+(index+1);
}
};
}
private final int _size;
}
}
この例では、まずvalueパラメータの取得から始めています。これは、表に現在表示されている先頭行の索引です。 DataObjectList
の索引はゼロから始まりますが、valueパラメータは1から始まるため、オフセット調整が必要です。
PagedDataObjectList
は、必要なブロック・サイズ(30)および現在の先頭の索引で作成されます。 最後に、このDataObjectList
をDataObject
に配置します。 DataObject
は、tableのvalue
属性、blockSize
属性、maxValue
属性およびtableData
属性にそれぞれバインドする必要のあるキー、value、size、maxValueおよびcurrentを実装します。 これは、次のUIX XMLで行います。
<table tableData="${uix.eventResult.tableData.current}"
value="${uix.eventResult.tableData.value}"
minValue="1"
maxValue="${uix.eventResult.tableData.maxValue}"
blockSize="${uix.eventResult.tableData.size}"
name="table1"
... >
...
</table>
この例では、ある程度間接的なデータ・バインディングを使用しています。 データ・バインディングの詳細は、「ADF UIXでのデータ・バインディング」を参照してください。
前述した例では、すべてのナビゲーション・リンクが現在のページをポイントしています。 一部のアプリケーションでは、ナビゲーション・リクエストを他のURLに向ける必要があります。 これには、tableにdestination
属性を設定します。 destination
には、イベント・パラメータを受け入れるサーバーのURLを整形式で指定する必要があります。 宛先が指定されている場合、表はレコード・ナビゲーション・リンクをページにレンダリングし、必要なレコード・ナビゲーション・イベントとともにURLパラメータを指定の宛先に送信します。 次に例を示します。
<table destination="http://www.oracle.com/eventHandler"
... >
...
</table>
表のイベントをサーバーに送信する別の方法として、表が通信にHTMLフォームを使用するよう指定する方法があります。 フォームを使用して得られる結果は、宛先URLを使用する場合と実際は同じで、サーバーはユーザーのアクションを示すキーと値のペアを受信しますが、フォームの使用には他にも利点があります。 フォームを使用してレコード・ナビゲーションなどの表でのアクションを送る場合、フォームの他のすべての値が、表のナビゲーション・パラメータとともにサーバーに送信されます。
例として、表のナビゲーション制御およびtextInput
の両方が同じページの、同じHTMLフォーム内にある場合を想定します。 ユーザーが表のナビゲーション・バー・リンクをクリックして新規の行セットに移動する場合、新規のページがユーザーに対して生成される際にそのコンテンツが保持および再表示されるよう、textInput
の値のサーバーへの送信が必要になる場合があります。 これを行うには、tableのformSubmitted
属性をtrue
に設定します。 このようにすると、ユーザーがナビゲーション領域のリンクをクリックすると、表はJavaScriptを介して表が属するフォームをフォーム内の他のすべての値とともに送ります。
しかし、表はどのようにして、ユーザーがどのレコード・ナビゲーション・イベントを選択したかを示す4つの必要なパラメータをサーバーに送信するのでしょうか。 これは、4つの特殊な非表示フィールドを、表自身も存在するHTMLフォーム内にレンダリングすることで行います。 フォームが表によって送信される前に、JavaScriptを使用してユーザーのアクションを示すようこれらの非表示フィールドの値が変更されます。 フォーム送信の使用例を次に示します。
<dataScope ... >
<provider>
...
</provider>
<contents>
<form name="testForm">
<contents>
<table formSubmitted="true"
... >
<contents>
...
</contents>
</table>
</contents>
</form>
</contents>
</dataScope>
フォーム送信は、フォームの入力要素が表の列スタンプとして使用される場合にも必要です。
レコード・ナビゲーションには他にも特殊なケースがあり、なんらかの理由で表に現在表示する行が存在しないケースなどがあります。 たとえば、用語の検索結果を表示する表で、検索条件に一致するものが含まれていないためにデータ行がないというケースがそうです。 このような表では、データが表示されない理由を示すメッセージを表示するとユーザーにわかりやすくなります。 このテキスト・メッセージは、次に示すようにalternateText
属性で設定します。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
<!-- no rows in this example! -->
...
</inline>
</data>
</provider>
<contents>
<table tableData="${uix.data.demoTableData.demoRowData}"
...
alternateText="(No search results were found)">
...
</table>
</contents>
</dataScope>
テキスト・メッセージは、tableにtableDataが設定されていない場合、またはtableDataで返される行数が0の場合にのみ表示されます。
列ヘッダーは、columnHeader
のノードとしてSortableHeaderBean
を使用することによりソート可能になります。 ただし、列がソート可能であるかは、追加情報を提供するまで明らかにはなりません。
ソート可能であることを示す方法でSortableHeaderBean
がレンダリングするには、sortable
属性に値を指定する必要があります。 この属性に指定できる値は3つあります。
UIX XML | UIConstant | 説明 |
---|---|---|
yes
| SORTABLE_YES
| 列はソート可能ですが、現在はソートされていないことを示します。 列ヘッダーは、その列の値に基づいてデータ項目をソートする場合に、ユーザーがその列ヘッダーを選択する必要があることを示すような方法でレンダリングします。 |
ascending
| SORTABLE_ASCENDING
| 列ヘッダーがソート可能としてレンダリングされる必要があり、表がすでにこの列でソートされているという通知をユーザーに表示します。 ソートされた列の値が、列を下に読み進むにつれて増えていくことも示します。 |
descending
| SORTABLE_DESCENDING
| 列ヘッダーがソート可能としてレンダリングする必要があり、表がすでにこの列でソートされていることをユーザーに通知します。 ソートされた列の値が、列を下に読み進むにつれて減っていくことも示します。 |
列の1つを降(昇)順にソート可能にすることで、この機能をデモンストレーションしてみます。 新規のSortableHeaderBean
のsortable
属性をこれらの値にデータ・バインディングすると、列ヘッダー・スタンプの外観がどのように変化するかに注意してください。
<dataScope ... >
<provider>
<data name="demoTableData">
<inline>
<!-- all the row DataObjects used by our table for tableData -->
<demoRowData ... />
...
</inline>
</data>
</provider>
<contents>
<form name="testForm">
<contents>
<table ... >
<contents>
<!-- the first column stamp, a text node -->
<column>
<columnHeader>
<!-- add a sortable column header stamp node -->
<sortableHeader text="First Header"
sortable="yes"/>
</columnHeader>
<contents>
<text text="${uix.current.firstColumnText}"/>
</contents>
</column>
<!-- the second column stamp, a button -->
<column>
<columnHeader>
<!-- add a sortable column header stamp node -->
<sortableHeader text="Second Header"
sortable="ascending"/>
</columnHeader>
<contents>
<button destination="http://www.oracle.com"
text="${uix.current.secondColumnText}"/>
</contents>
</column>
</contents>
</table>
</contents>
</form>
</contents>
</dataScope>
ソート可能なヘッダーをクリックすると、表はUIXサーブレットのUIConstants.SORT_EVENT
というイベント(UIX XMLではsort)を生成します。 このイベントには、次の表で説明する3つのパラメータがあります。
イベント・パラメータ | UIConstant | 説明 |
---|---|---|
source | SOURCE_PARAM |
このイベントを生成する表を示します。 これは、それぞれの表のname です。
|
value | VALUE_PARAM |
列ヘッダー・セルのSortableHeaderBean のvalue 属性として提供される任意のものを示します。 デフォルト値は、列のゼロから始まる索引です。
|
state | STATE_PARAM |
列ヘッダー(valueパラメータで指定)が、すでにソートされているかどうかを示します。 すでにソートされている場合、このパラメータは、列のソート方法に応じてUIConstant SORTABLE_ASCENDING またはUIConstant SORTABLE_DESCENDING になります。 列がまだソートされていない場合、state値としては何も送信されません。 |
次のセクションでは、サーバー上でソートを処理する方法の例を示します。 この例は、一般的な環境で動作するよう設計されているため、本来必要である以上に複雑になっています。
最初の手順は、SortableHeaderBean
のvalue
属性の指定です。 各列ヘッダーのvalue
属性として、列データに使用するものと同じキーを使用します。 サーバーでは、valueパラメータを取得し、それを表の行のDataObject
でキーとして使用することにより、ソートが必要な列データを取得できるため、列データと同じキーを使用することには意味があります。 次のUIX XMLは、この部分を示しています(name、ageおよびbloodという3つのソート可能な列があります)。
<table ... >
<contents>
<column>
<columnHeader>
<sortableHeader text="Name" value="name" sortable=... >
</sortableHeader>
</columnHeader>
<contents>
<text text="${uix.current.name}"/>
</contents>
</column>
<column>
<columnHeader>
<sortableHeader text="Age" value="age" sortable=... >
</sortableHeader>
</columnHeader>
<contents>
<text text="${uix.current.age}"/>
</contents>
</column>
<column>
<columnHeader>
<sortableHeader text="Blood" value="blood" sortable=... >
</sortableHeader>
</columnHeader>
<contents>
<text text="${uix.current.blood}"/>
</contents>
</column>
<column>
<columnHeader>
<sortableHeader text="Phone">
</sortableHeader>
</columnHeader>
<contents>
<text text="${uix.current.phone}"/>
</contents>
</column>
</contents>
</table>
次の手順は、サーバー上でのsortイベントの処理です。 次のコードでは、valueパラメータおよびstateパラメータを取得し、EventResult
に格納します。
public static EventResult doSortEvent(BajaContext bc, Page page,
PageEvent event)
{
EventResult result = new EventResult(page);
result.setProperty(UIConstants.VALUE_PARAM,
event.getParameter(UIConstants.VALUE_PARAM));
// if we are already sorting in ascending order, then we want to sort in
// descending order. Otherwise, sort in ascending order
Object state = event.getParameter(UIConstants.STATE_PARAM);
result.setProperty(UIConstants.STATE_PARAM,
UIConstants.SORTABLE_ASCENDING.equals(state)
? UIConstants.SORTABLE_DESCENDING
: UIConstants.SORTABLE_ASCENDING);
return result;
}
次に、各列ヘッダーのsortable
属性を決定するDataObject
を作成する必要があります。 次のDataObject
では、現在の列ヘッダーのvalue
属性をkeyとしてコールします。 続いて、keyをEventResult
上のvalue(前述のイベント・ハンドラで設定)と比較することによって、これが、ソートされた列のヘッダーであるかどうかを確認します。 適切な列ヘッダーである場合は、state(昇順または降順)を返します。適切な列ヘッダーでない場合は、nullを返します。
private static final DataObject _SORT_COLUMN_HEADER = new DataObject() {
public Object selectValue(RenderingContext rc, Object key)
{
BajaContext bc = BajaRenderingContext.getBajaContext(rc);
EventResult er = EventResult.getEventResult(bc);
if (er!=null)
{
// check to see if it is this column that has been sorted. We assume
// that "key" is the "value" attribute of this column header.
if (key.equals(er.getProperty(UIConstants.VALUE_PARAM)))
return er.getProperty(UIConstants.STATE_PARAM);
}
return null;
}
};
次の手順では、このDataObject
を使用するために、SortableHeaderBean
のsortable
属性をデータ・バインドします。 最初に、このDataObject
を使用して値の取得を試行しますが、それに失敗した場合には、デフォルトでsortable
をyesに設定します。 これは、次のUIX XMLコードにより処理されます。
<table ... >
<column>
<columnHeader>
<!-- first try to get the value from the sortColumnHeader
dataObject. if the boundValue returns null, use sortable=yes-->
<sortableHeader text="Name"
value="name"
sortable="${ui:defaulting(uix.data.sortColumnHeader.name, 'yes')}">
</sortableHeader>
</columnHeader>
<contents>
<text text="${uix.current.name}"/>
</contents>
</column>
<column>
<columnHeader>
<!-- first try to get the value from the sortColumnHeader
dataObject. if the boundValue returns null, use sortable=yes-->
<sortableHeader text="Age"
value="age"
sortable="${ui:defaulting(uix.data.sortColumnHeader.age, 'yes')}">
</sortableHeader>
</columnHeader>
<contents>
<text text="${uix.current.age}"/>
</contents>
</column>
<column>
<columnHeader>
<!-- first try to get the value from the sortColumnHeader
dataObject. if the boundValue returns null, use sortable=yes-->
<sortableHeader text="Blood Group"
value="blood"
sortable="${ui:defaulting(uix.data.sortColumnHeader.blood, 'yes')}">
</sortableHeader>
</columnHeader>
<contents>
<text text="${uix.current.blood}"/>
</contents>
</column>
<column>
<columnHeader>
Phone
</columnHeader>
<contents>
<text text="${uix.current.phone}"/>
</contents>
</column>
...
</table>
ui:defaulting
UIX Components関数では、最初の引数から値の取得を試行しますが、失敗した場合には2番目の引数の値を返します。 上の例では、最初の引数がsortColumnHeaderデータ・プロバイダから値の取得を試行し(このデータ・プロバイダは、前述の例で作成した_SORT_COLUMN_HEADER
にバインドされていると想定)、2番目の引数がデフォルトです。
ここまでの処理を整理します。 ソート可能なヘッダーは、クリックに対応しているのみでなく、クリックされるたびにソート表示の昇順と降順を切り換えます。 ただし、列はまだソートされません。次のセクションでは、実際のソート処理を実装します。
public static DataObject getSortedTableData(RenderingContext rc,
String namespace, String name)
{
DataObject[] data;
BajaContext bc = BajaRenderingContext.getBajaContext(rc);
EventResult er = EventResult.getEventResult(bc);
if (er!=null)
{
// we need to clone because we are going to mutate the array:
data = (DataObject[]) _TABLE_DATA.clone();
Object state = er.getProperty(UIConstants.STATE_PARAM);
Object key = er.getProperty(UIConstants.VALUE_PARAM);
Comparator comp = new DataObjectComparator(
rc,
key, // this is the key to sort the DataObjects on
UIConstants.SORTABLE_ASCENDING.equals(state));
Arrays.sort(data, comp);
}
else
data = _TABLE_DATA;
return new ArrayDataSet(data);
}
このデータ・プロバイダは、イベント・ハンドラdoSortEvent(...)
によりEventResult
に設定されたstateおよびvalueに従ってソートされた、DataObjectList
を返します。 _TABLE_DATA
は、ソートされていないデータです。 実際のソートは、java.util.Arrays
クラスにより処理されます。 DataObjectComparator
クラスにも注意してください。
private static final class DataObjectComparator implements Comparator
{
public DataObjectComparator(RenderingContext context,
Object key, boolean ascending)
{
_context = context;
_key = key;
_ascending = ascending;
}
public int compare(Object o1, Object o2)
{
DataObject dob1 = (DataObject) o1;
DataObject dob2 = (DataObject) o2;
Comparable val1 = (Comparable) dob1.selectValue(_context, _key);
Object val2 = dob2.selectValue(_context, _key);
int comp = val1.compareTo(val2);
// if we are not sorting in ascending order, then negate the comparison:
return _ascending ? comp : -comp;
}
private final RenderingContext _context;
private final Object _key;
private final boolean _ascending;
}
このクラスでは、2つのDataObject
の比較を処理し、_key
を使用して両方の値を取得します。 比較は、値がComparable
であると想定して行っています(降順でソートしている場合は結果を反転します)。
特定の行を処理するために一括選択する機能は、多くのアプリケーションで役に立ちます。 Table
では、tableSelection
という子を介して選択をサポートします。 UIXには、SingleSelectionBean
およびMultipleSelectionBean
の2つの選択Beanが用意されています。 SingleSelectionBean
は、1行選択のみ可能な表で使用し、MultipleSelectionBean
は複数行選択が可能な場合に使用します。 説明に進む前に、これらのBeanが正しく動作するためには、フォーム内部でTable
を使用する必要があることに注意してください。 これらのBeanが、フォーム要素を使用してサーバーに選択項目を指示するためです。
表での単一選択は、tableSelection
の子としてSingleSelectionBean
を使用することにより処理されます。 次に、単純な例を示します。
<table name="table1" ... >
<tableSelection>
<singleSelection/>
</tableSelection>
...
</table>
最初は、どの行も選択済としてマークされていないことに注意してください。 初期選択項目は、選択が必要な行の(ゼロから始まる)索引を、singleSelection
のselectedIndex
属性で指定することにより設定できます。
<singleSelection selectedIndex="1" />
次の手順は、選択項目に対してどんな処理が可能であるかをユーザーに知らせることです。 これには、singleSelection
のtext
属性を使用します。 ユーザーが処理を開始する際にボタンをクリックできるように、submitButton
をレイアウトに追加する必要もあります。次の例で示すように、ボタンは索引付けされた子として選択Beanに追加できます。
<tableSelection>
<singleSelection text="Select record and ..."
selectedIndex="1" >
<contents>
<submitButton text="Copy" event="copy" />
<submitButton text="Delete" event="delete" />
</contents>
</singleSelection>
</tableSelection>
サーバー上での選択の処理方法を説明します。 上の例のsubmitButton
は、どちらもサーバーでのイベントを起動しています。 次に、イベント・ハンドラを示します(ここで注意するポイントは、選択した索引の取得にoracle.cabo.ui.beans.table.SelectionUtils
を使用していることです)。
public static EventResult doSelectionEvent(BajaContext bc, Page page,
PageEvent event)
{
DataObject tableRows = new PageEventFlattenedDataSet(event, "table1");
int index = SelectionUtils.getSelectedIndex(tableRows);
String name = "Nothing Selected";
// make sure that something was selected:
if (index>=0)
{
DataObject row = _TABLE_DATA.getItem(index);
name = row.selectValue(null, "name").toString();
}
EventResult result = new EventResult(page);
result.setProperty("action", event.getName());
result.setProperty("name", name);
return result;
}
表での複数選択は、tableSelection
の子としてMultipleSelectionBean
を使用することにより処理されます。 singleSelection
と同様に、multipleSelection
もtext
属性および索引付けされた子をサポートします。 次に例を示します。
<table ... >
<tableSelection>
<multipleSelection text="Select record and ...">
<contents>
<submitButton text="Copy" event="copy"/>
<submitButton text="Delete" event="delete" />
</contents>
</multipleSelection>
</tableSelection>
...
</table>
multipleSelection
のselected
属性およびselection
属性をデータ・バインディングすることにより、初期選択項目を設定できます。 MultipleSelectionBean
が列にスタンプされるたびに、そのselected
属性が評価されます。 このときに、selection DataObjectList
の対応する行が現行のDataObject
になります。
<multipleSelection selected="${uix.data.selectedKey}" ...>
<selection>
<!-- create a dataObjectList, each dataObject has a selectedKey
whose value is either true or false -->
<row selectedKey="true"/> <!-- select the first row -->
<row selectedKey="false"/>
<row selectedKey="true"/> <!-- select the third row -->
...
</selection>
...
</multipleSelection>
この例では、selection
属性の値をインラインで設定していますが、これは(前述のとおり)簡単にデータ・バインドできます。
複数選択は、サーバーでSelectionUtils.getSelectedIndices(...)
メソッドを使用して処理されます。 このメソッドは配列を返します。配列の各要素は、選択された行の索引です。 次に、イベント・ハンドラのコードを示します。
public static EventResult doMultiSelectEvent(BajaContext bc, Page page,
PageEvent event)
{
PageEventFlattenedDataSet tableRows =
new PageEventFlattenedDataSet(event, "table1");
int[] indices = SelectionUtils.getSelectedIndices(tableRows);
DataObjectList resultTableData = new SelectedList(_TABLE_DATA, indices);
EventResult result = new EventResult(page);
result.setProperty("action", event.getName());
result.setProperty("tableData", resultTableData);
return result;
}
SelectedList
クラスは、便利なユーティリティ・クラスです。このクラスは、DataObjectList
および選択された索引の配列を受け取り、選択されたDataObject
のみを含むDataObjectList
を実装します。
private static final class SelectedList implements DataObjectList
{
public SelectedList(DataObjectList data, int[] selectedIndices)
{
_data = data;
_indices = selectedIndices;
}
// returns the number of selected rows
public int getLength()
{
return _indices.length;
}
// gets the selected row at the given index.
public DataObject getItem(int index)
{
return _data.getItem(_indices[index]);
}
private final DataObjectList _data;
private final int[] _indices;
}
multipleSelection
には、「すべて選択」/「選択しない」、という機能があります。 通常これは、現在のレコード・セットに表示されている表の行を選択(または選択解除)することを意味します。 ただし一部のアプリケーションでは、(現在表示されている行のみでなく)レコード・セットのすべての行でこの操作を実行する必要があります。 これを実行可能にするため、multipleSelection
ではselectModeというフォーム・パラメータを追加します。 ユーザーが「すべて選択」/「選択しない」をクリックしていない場合、このパラメータの値は空になります。 ユーザーが「すべて選択」をクリックすると、このパラメータの値はallになります。 ユーザーが「選択しない」をクリックすると、この値はnoneになります。 サーバー上のイベント処理コードは、ユーザーが「すべて選択」/「選択しない」をクリックしたかどうかを、このパラメータを使用して判断します。
singleSelection
およびmultipleSelection
は、どちらもdisabled
属性をサポートします。 選択Beanが列にスタンプされる際に、disabled
属性が評価され、Boolean.TRUE
が返された場合は、その行に対する選択機能が使用不可になります(表の現在の行のDataObject
が現行のDataObject
になります)。
<dataScope>
<provider>
<data name="tableData">
<inline>
<row name="Person 1" age="11" disabledKey="true" sel="true"/>
<row name="Person 2" age="12" disabledKey="false"/>
<row name="Person 3" age="13" sel="true"/>
<row name="Person 4" age="14"/>
<row name="Person 5" age="14" disabledKey="true"/>
</inline>
</data>
</provider>
<contents>
<form name="form1">
<contents>
<table tableData="${uix.data.tableData.row}"... >
<tableSelection>
<multipleSelection ...
disabled="${uix.current.disabledKey}">
</multipleSelection>
</tableSelection>
...
</table>
</contents>
</form>
</contents>
</dataScope>
一部のアプリケーションでは、表の行に特定要素のプロパティの概要のみが表示されます。 このような場合、ボタンをクリックすることで、その行の詳細情報が表示されるようにすると便利です。 TableBean
のディテール公開機能により、この機能が提供されます。
ディテール公開は、table
のdetail
という名前の付けられた子を設定することによりオンになります。 この子は、ユーザーが行の詳細を要求したときのみレンダリングされる点を除き、他のスタンプと同様です。 したがって、この子は現在の行の詳細をすべて表示できます。 次に例を示します。
<table ... >
<detail>
<!-- this is the detailed stamp -->
<labeledFieldLayout>
<contents>
Name
<styledText text="${uix.current.name}" styleClass="OraDataText"/>
Age
<styledText text="${uix.current.age}" styleClass="OraDataText"/>
Blood Group
<styledText text="${uix.current.blood}" styleClass="OraDataText"/>
Phone
<styledText text="${uix.current.phone}" styleClass="OraDataText"/>
</contents>
</labeledFieldLayout>
</detail>
<contents>
<!-- these are the regular column stamps -->
<column>
<columnHeader>Name</columnHeader>
<contents>
<text text="${uix.current.name}"/>
</contents>
</column>
<column>
<columnHeader>Age</columnHeader>
<contents>
<text text="${uix.current.age}"/>
</contents>
</column>
</contents>
</table>
この例では、通常の表に各行のNameプロパティおよびAgeプロパティのみ表示されます。 ユーザーが詳細を要求すると、detail
スタンプがレンダリングされ、Blood GroupやPhoneなどの追加情報が提供されます(detail
という子は、現行のDataObject
として表の現在の行であるDataObject
でレンダリングされます。そのため、データ・バインディングは、列スタンプと同じ方法で動作します)。
ただし、この例では、詳細セクションが公開されていません。 このため、レンダリングされたdetail
スタンプを参照できませんでした。 UIとしてレンダリングされる各矢印の公開状態は、table
のdetailDisclosure
属性により制御されます。 これは、表の各行に対応するDataObject
を持つDataObjectList
である必要があります。 各DataObject
は、キーUIConstants.DISCLOSED_KEY
(またはUIX XMLではdisclosed
)で問合せされます。 この問合せの結果がBoolean.TRUE
である場合は、その行の詳細情報が公開されます。 次に例を示します。
<table ... >
...
<detailDisclosure>
<row disclosed="false"/>
<row disclosed="false"/>
<row disclosed="true"/> <!-- disclose the third row -->
<row disclosed="false"/>
<row disclosed="false"/>
<row disclosed="true"/> <!-- disclose the sixth row -->
</detailDisclosure>
</table>
前の例では、ディテール公開矢印は実際には何も行いません。これらの例は形式のみのものです。 インタラクティブ・デモの作成方法を説明します。 矢印をクリックすると、詳細を表示するか非表示にするかに応じて、それぞれUIConstants.SHOW_EVENT
またはHIDE_EVENT
というUIXサーブレットのイベント(UIX XMLではshowまたはhide)が生成されます。 イベントには次の2つのパラメータがあります。
パラメータ | UIConstant | 説明 |
---|---|---|
source | SOURCE_PARAM
| このイベントを生成した表を示します。 表のname 属性です。
|
value | VALUE_PARAM
| 公開する(または非公開にする)必要のある行を示します。 ゼロから始まる索引です。 |
valueパラメータにより、公開または非公開にする必要のある行が簡単にわかります。 ただし、すでに公開されている行に関する十分な情報は得られません。 表の現在の公開状態を保存するには、クライアント側のページに状態を追加する必要があります。 この例では、disclosed
という名前の非表示フォーム・パラメータを詳細セクションに追加します(このため、表はフォーム送信モードで使用されている必要があります)。
<table formSubmitted="true" ... >
...
<detail>
<labeledFieldLayout>
<contents>
...
<formValue name="disclosed" value="1"/>
</contents>
</labeledFieldLayout>
</detail>
</table>
次のイベント・ハンドラでは、ユーザーが選択した行の索引を取得し、イベント名に応じて公開するか非公開にするかを決定します。
public static EventResult doHideShowEvent(BajaContext bc, Page page,
PageEvent event)
{
PageEventFlattenedDataSet tableRows =
new PageEventFlattenedDataSet(event, "table1");
// this is the row that must be (un)disclosed:
int row = Integer.parseInt(event.getParameter(UIConstants.VALUE_PARAM));
// decide whether we want to disclose or undisclose depending on the name
// of the event
boolean disclose = UIConstants.SHOW_EVENT.equals(event.getName());
DataObjectList detailData = new DetailData(tableRows, row, disclose);
EventResult result = new EventResult(page);
result.setProperty("detailData", detailData);
return result;
}
このコードでは、表の現在の公開状態を含むPageEventFlattenedDataSet
を作成します。 これを使用してdetailData DataObjectList
を作成します。 このリストには、ツリーの以前の公開状態および新規状態が含まれます。 このリストはEventResult
に挿入されるため、UIX XMLコードからアクセスできます。 DetailData
クラスを次に示します。
private static final class DetailData implements DataObjectList
{
/**
* @param pageEvent contains the current disclosure state of the table
* @param index the index of the row that must have its disclosure state
* changed
* @param disclosure the new disclosure state for the row
*/
public DetailData(DataObjectList pageEvent, int index, boolean disclose)
{
_pageEvent = pageEvent;
// initially, none of the table rows will be disclosed, so there will be
// no pageEvent data and this length would be zero:
_length = pageEvent.getLength();
_index = index;
_disclose = disclose;
}
public int getLength()
{
// make sure that the length we return is sufficiently large enough that
// we reach the index we want to change
return (_index >= _length) ? _index+1 : _length;
}
public DataObject getItem(int index)
{
boolean disclose;
if (index==_index)
{
// this is the index that we want to change.
disclose = _disclose;
}
else if (index < _length)
{
// this index can safely be pulled from the pageEvent
DataObject row = _pageEvent.getItem(index);
// if there was a "disclosed" form element on this row then we
// consider the row disclosed:
disclose = (row.selectValue(null, "disclosed") != null);
}
else
disclose = false;
return disclose ? _DISCLOSE_TRUE : _DISCLOSE_FALSE;
}
private final DataObjectList _pageEvent;
private final int _index, _length;
private final boolean _disclose;
private static final DataObject _DISCLOSE_TRUE = new DataObject() {
public Object selectValue(RenderingContext rc, Object key)
{
return Boolean.TRUE;
}
};
private static final DataObject _DISCLOSE_FALSE = new DataObject() {
public Object selectValue(RenderingContext rc, Object key)
{
return Boolean.FALSE;
}
};
}
基本的に、このクラスは、表の現在の公開状態によりどの行が公開されているかを判断し、true(公開されている場合)またはfalse(公開されていない場合)の適切なDataObject
を返します。 指定された索引が公開状態の変更対象の行である場合は、変更された新しい公開状態が返されます。 PageEventFlattenedDataSet
は、表のフォーム入力要素が設定されている場合にのみデータを含むことに注意してください。 初期状態では、何も公開されていないため、レンダリングされる入力要素はなく、FlattenedDataSet
はその長さとしてゼロを返します。 したがって、この特殊なケースは慎重に処理する必要があります。
場合によっては、データ領域の下部にある特別なセクションに、表のデータを要約して入れると適切な場合があります。 これはfooter
と呼ばれ、表の名前の付けられた子です。 フッターは、表の一番下に1回のみレンダリングされます。 ほとんどのフッターは、表全体の統計や処理を要約するために使用されます。 フッターを使用して列の統計や処理を要約する必要がある場合は、列フッターを使用します。 table
に加えてcolumn
にも、footer
という名前の付けられた子を使用できます。
表フッターとしてのみ使用される特殊なUINode
には、addTableRow
およびtotalRow
の2つがあります。 次に、この2つについて詳しく説明します。また、列フッターについても説明します。
AddTableRow
Beanまず、addTableRow
です。 このフッター・ノードは、ユーザーが実行時にデータ行を追加できるようにする表で使用するためのものです。 追加する行数や表示するテキストのカスタマイズ、URLリンク先の変更を行うことができます。 ただし、指定されていない場合はすべてデフォルトになります。 次に、使用例を示します。
<table ... >
...
<!-- footer node -->
<footer>
<addTableRow rows="5"/>
</footer>
</table>
この例のaddTableRow
では、ユーザーに5行を追加するように求めるボタンがレンダリングされます。 rows
属性に値を指定していない場合は、rows
属性にはデフォルトの1が使用されます。行は、ユーザーが「表の行の追加」ボタンを押しても、すぐには追加されません。かわりに、UIConstants.ADD_ROWS_EVENT
(UIX XMLではaddRows
)イベントがサーバーに送信されます。 サーバーで、ページのデータソースを更新して、ページを再生成する必要があります。 次に、UIConstants.ADD_ROWS_EVENT
(UIX XMLではaddRows
)イベントでサーバーに送信されるパラメータを示します。
パラメータ | UIConstant | 説明 |
---|---|---|
source | SOURCE_PARAM
| 表の名前 |
size | SIZE_PARAM
| 追加するよう要求された行数 |
TotalRow
Bean前述のBeanと似ているのはtotalRow
です。 行を追加するかわりに、このBeanを使用して表の数値列の合計を更新します。 ただし、このBean自体は合計を計算しません。
totalRow
では、表フッターにボタンおよびテキストがレンダリングされます。 ボタンのテキストは、Beanのtext
属性を使用して構成できます。 列に合計フィールドを配置するには、column
の名前の付けられた子footer
を使用します。 totalRow
ボタンをアクティブにすると、イベントがサーバーに送信されます。このときのイベントはUIConstants.UPDATE_EVENT
(UIX XMLではupdate)です。
パラメータ | UIConstant | 説明 |
---|---|---|
source | SOURCE_PARAM
| 表の名前 |
次に、table
のfooter
という名前の付けられた子であるtableFooter
Bean内でtotalRow
を使用した例を示します。また、この例では、右端のcolumn
のfooter
という名前の付けられた子でtextInput
フィールドを使用しています。
<table ... >
<contents>
<column>
<columnHeader>First Header</columnHeader>
<contents>
<!-- the first column stamp, a text node -->
<textInput name="foo" text="${uix.current.firstColumnText}"/>
</contents>
</column>
<column>
<!-- column footer is a textInput. This is used with totalRow -->
<footer>
<textInput columns="5" name="total" text="42"/>
</footer>
<columnHeader>Second Header</columnHeader>
<!-- the second column stamp, a button -->
<contents>
<button destination="http://www.oracle.com"
text="${uix.current.secondColumnText}"/>
</contents>
</column>
</contents>
<!-- table footer is a totalRow -->
<footer>
<tableFooter>
<total>
<totalRow/>
</total>
</tableFooter>
</footer>
</table>
ここでも、更新ボタンがアクティブな場合に、合計を表すノードに対する実際の更新は、表の作成者が実行する必要があります。
場合によっては、表にtotalRow
およびaddTableRow
の両方が必要です。 次に例を示します。
<table ... >
<contents>
<column>
<columnHeader>First Header</columnHeader>
<contents>
<!-- the first column stamp, a text node -->
<textInput name="foo" text="${uix.current.firstColumnText}"/>
</contents>
</column>
<column>
<!-- column footer is a textInput. This will be used with totalRow -->
<footer>
<textInput columns="5" name="total" text="42"/>
</footer>
<columnHeader>Second Header</columnHeader>
<!-- the second column stamp, a button -->
<contents>
<button destination="http://www.oracle.com"
text="${uix.current.secondColumnText}"/>
</contents>
</column>
</contents>
<!-- table footer contains both addTableRow and totalRow -->
<footer>
<tableFooter>
<total>
<totalRow/>
</total>
<contents>
<addTableRow/>
</contents>
</tableFooter>
</footer>
</table>
これまでは、表のセルの実際のコンテンツ、およびサーバーと通信するいくつかの処理についてのみ扱ってきました。 データの構造を明確にするため、または読みやすくするために、表の外観または書式を変更することが必要な場合があります。 表の書式をカスタマイズする方法は多数あり、その組合せの数も膨大になります。 幸い、これらの異なるカスタマイズを実行するメカニズムは単純です。
表の外観を変更する最も簡単な方法は、表の幅を変更することです。表の幅は、表のwidth
属性を設定するだけで変更できます。 幅は、ピクセル単位または親要素の幅に対するパーセントで指定できますが、一般にはパーセントが好まれます。 ただし、表の幅に指定した値が、コンテンツを十分に収容できる大きさでない場合は、表示の際に上書きされ、必要な最低容量が使用されます。
次に、列の書式設定について説明します。 ユーザー・インタフェースの設計に応じて、タイプの異なるコンテンツの配置方法を区別し、ユーザーが理解しやすいようにする必要があります。 この場合、位置揃えは特定の列のセル全体で統一されている必要があるため、列のcolumnFormat
属性を使用します。 次に最初の例を示します。この例では、列データの書式を設定します。
<dataScope ... >
<provider>
...
</provider>
<contents>
<form ... >
<contents>
<table ...
width="100%">
<contents>
<!-- the first column stamp, a text node -->
<column>
<columnHeader>First Header</columnHeader>
<contents>
<textInput text="${uix.current.firstColumnText}" name="foo"/>
</contents>
</column>
<!-- a second column stamp, a static text node -->
<column>
<columnHeader>Second Header</columnHeader>
<columnFormat columnDataFormat="numberFormat"/>
<contents>
<text text="42"/>
</contents>
</column>
<!-- the third column stamp, a button -->
<column>
<columnHeader>Third Header</columnHeader>
<columnFormat columnDataFormat="iconButtonFormat"/>
<contents>
<button text="${uix.current.secondColumnText}" destination="http://www.oracle.com"/>
</contents>
</column>
</contents>
...
</table>
</contents>
</form>
</contents>
</dataScope>
この例ではいくつかの項目を変更しました。順番に説明します。 表の幅は100%に設定しました。 続いて、表に別の列スタンプ(テキスト・ノード)を追加して、処理内容を増やしました。 さらに、2番目と3番目のcolumn
に複雑属性columnFormat
を追加して、columnDataFormat
属性を設定しました。
この例では、最初の列には書式を指定していません。そのため、テキストのコンテンツの位置揃えはデフォルトの左揃えになります。 2番目の列では、columnDataFormat
がnumberFormat
に設定されています。 仕様では、数値は右揃えにするよう規定されているため、この表の列のデータ・セルはそれに従って配置されます。 最後に、3番目の列では、columnDataFormat
属性がiconButtonFormat
に設定されています。現在、この設定はコンテンツを中央揃えにします。中央揃えは、アイコンまたはボタンの列に対して適切な位置揃えです。 これは、前の例を参照するとよくわかります。
columnFormat
属性は5つあります。 次の例では、これらのほとんどを使用しています。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
...
</inline>
</data>
</provider>
<contents>
<table name="myTestTable" width="100%"
tableData="${uix.data.demoTableData.demoRowData}">
<contents>
<!-- the first column stamp, a text node -->
<column>
<columnHeader>First Header</columnHeader>
<columnFormat cellNoWrapFormat="true" />
<contents>
<textInput name="foo" text="${uix.current.firstColumnText}"/>
<!-- a second column stamp, a static text node -->
</contents>
</column>
<column>
<columnHeader>Second Header</columnHeader>
<columnFormat columnDataFormat="numberFormat" displayGrid="false" />
<contents>
<text text="42"/>
</contents>
</column>
<column>
<columnHeader>Third Header</columnHeader>
<columnFormat columnDataFormat="iconButtonFormat" width="100%"/>
<contents>
<!-- the third column stamp, a button -->
<button destination="http://www.oracle.com"
text="${uix.current.secondColumnText}"/>
</contents>
</column>
</contents>
</table>
</contents>
</dataScope>
この例に基づき、columnFormat
で可能なすべての書式についてまとめると、次のようになります。
属性 UIConstant |
説明 |
---|---|
columnDataFormatCOLUMN_DATA_FORMAT_KEY
| 表自体にはコンテンツのタイプを認識する機能がないため、この属性を使用して、表の列に表示されるコンテンツのタイプを指定します。 異なるデータ書式を指定すると、データ・セルの位置揃えが変更されます。
|
cellNoWrapFormatCELL_NO_WRAP_FORMAT_KEY
| セルのコンテンツは、複数の行にまたがって表示しないほうがよい場合もあります。 折返しをしないほうが適切なデータ列については、この属性をtrue(Boolean.TRUE )にします。 折返しを禁止すると表の幅が広くなりすぎることがあるため、これはデフォルトの動作ではありません。
|
widthWIDTH_KEY
| 表の余分な幅のうち、この列が取得する幅のパーセントを指定するために使用されます。 表にコンテンツで必要とされる以上の領域がある場合は、その領域を列に配分します。 ただし、一部の列(ボタンなど)には、与えられた最小の領域よりも多くの領域は必要ありません。 テキスト列などのその他の列で、コンテンツの表示に必要な行を削減できる場合は、追加の領域を使用するほうが理想的です。 width属性のとる値は、50%や100%などのパーセント値で、列が取得する追加領域の割合を示します。 すべての列の合計は100%になる必要があります。 |
displayGridDISPLAY_GRID_KEY
| デフォルトでは、グリッド線はすべての列の左に表示されます。 場合によっては、垂直のグリッド線の数を減らし、他の列間の関係と比較して一部の列間の関係を強調するようにする場合もあります。 特定のデータ列の前、つまり左側の垂直グリッド線をオフにする場合は、この属性をfalseに設定します。 |
bandingShadeBANDING_SHADE_KEY
| 列のバンドの濃淡。 詳細は、後述の「バンド」のセクションを参照してください。
|
これらすべての列書式の値は厳格なルールではなく、ヒントにすぎません。 一部のレンダリングの外観やプラットフォームでは、すべての書式の値がサポートされているとはかぎりません。そのため、あるエージェントで特定のルールが通用しない場合もあります。 書式は依存性に欠けるため、アプリケーションにとって重要ではありません。
垂直グリッド線の表示場所を列で制御できることと同様に、行の水平グリッド線の表示場所をカスタマイズすることもできます。 このカスタマイズは、次の例で示すように、表のrowFormats
属性を使用して行います。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
...
<!-- DataObjectList to provide row formatting information -->
<demoRowFormats/>
<demoRowFormats displayGrid="false"/>
</inline>
</data>
</provider>
<contents>
<form ... >
<contents>
<table ...
rowFormats="${demoTableData.demoRowFormats}">
...
</table>
</contents>
</form>
</contents>
</dataScope>
この例では、2行目の上に水平グリッドを表示しないよう要求する行書式DataObject
を追加しました。 この機能が使用されることはほとんどありませんが、このように正常に動作します。
まれなケースとして、columnHeaderFormats
およびrowHeaderFormats
が使用できる場合もあります。これらは、tableに属性として設定できるDataObjectList
でもあります。 これらのリストDataObject
が応答する唯一のキーは、前に述べたcellNoWrapFormatキーです。 各DataObject
は、列ヘッダーまたは行ヘッダーに対応し、コンテンツを折り返す機能を制御します。 この場合も、折返しを禁止すると表の幅が広くなりすぎることがあるため、デフォルトでは折返しが許可されます。 これらの書式属性は、使用をなるべく控える必要があります。
最後に説明する書式タイプは、バンドです。 バンドとは、セルまたは列のコンテンツが視覚的にグループ化されるよう、表のセルまたは行の色を変更する機能のことです。 バンドを制御する方法は2通りあります。 まず、最も簡単で一般的な方法は、tableFormat DataObject
を使用して表全体のバンドを指定することです。 この単一書式オブジェクトは、表全体に適用されるため、DataObjectList
ではなくDataObject
となっています。
デフォルトでは、表にはバンドがありませんが、tableFormat DataObject
が表に設定されている場合は、キーtableBanding(UIConstants.TABLE_BANDING_KEY
)で問合せされます。 次の例で示すように、DataObject
が既知の値columnBanding(UIConstants.COLUMN_BANDING
)を返す場合は、表の列を変更すると、異なる背景色で表示されます。
<dataScope ... >
<provider>
<data name="demoTableData">
<inline>
...
<!-- DataObject for table-wide formatting (i.e. banding) -->
<demoTableFormat tableBanding="columnBanding"/>
</inline>
</data>
</provider>
<contents>
<form ... >
<contents>
<table ...
tableFormat="${demoTableData.demoTableFormat}">
...
</table>
</contents>
</form>
</contents>
</dataScope>
同様に、次の例で示すように、値rowBanding(UIConstants.ROW_BANDING
)を返すと、表の行の背景色が変更されます。 bandingInterval(UIConstants.BANDING_INTERVAL_KEY
)キーで問合せされた際に整数を返して、変更される各バンドの行数を制御することもできます。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<provider>
<data name="demoTableData">
<inline>
...
<!-- DataObject for table-wide formatting (i.e. banding) -->
<demoTableFormat tableBanding="rowBanding"/>
</inline>
</data>
</provider>
<contents>
...
</contents>
</dataScope>
ただし、列バンドは任意のパターンでも実行できます。 これにより、個々の列を、通常の変更パターンを使用せずに明るい色にすることができます。 このためには、列の複雑属性columnFormat
のbandingShade
属性をdarkまたはlightに設定します。 デフォルトはdarkです。 次の例では、第3列のみが明るいバンド色で表示されます。
<dataScope ... >
<provider>
<data name="demoTableData">
<inline>
...
<!-- DataObjectList to provide row formatting information -->
<demoRowFormats/>
<demoRowFormats displayGrid="false"/>
<!-- DataObject for table-wide formatting (i.e. banding) -->
<demoTableFormat tableBanding="rowBanding"/>
</inline>
</data>
</provider>
<contents>
<table name="myTestTable" width="100%"
tableData="${uix.data.demoTableData.demoRowData}">
<contents>
<column>
<columnHeader>First Header</columnHeader>
<columnFormat cellNoWrapFormat="true" />
<contents>
<!-- the first column stamp, a textInput node -->
<textInput name="foo" text="${uix.current.firstColumnText}"/>
</contents>
</column>
<column>
<columnHeader>Second Header</columnHeader>
<columnFormat columnDataFormat="numberFormat" displayGrid="false" />
<contents>
<!-- a second column stamp, a static text node -->
<text text="42"/>
</contents>
</column>
<column>
<columnHeader>Third Header</columnHeader>
<columnFormat columnDataFormat="iconButtonFormat" width="100%" bandingShade="light"/>
<contents>
<!-- the third column stamp, a button -->
<button destination="http://www.oracle.com"
text="${uix.current.secondColumnText}"/>
</contents>
</column>
</contents>
</table>
</contents>
</dataScope>
任意の列書式オブジェクトに対してバンドが明示的に設定されている場合は、表全体の書式が完全に無視されるため注意してください。 表全体のバンドを明示的な列バンドと組み合せることはできません。組み合せると、正常な配色パターンが生成されません。
TableStyle
クラスoracle.cabo.ui.beans.table.TableStyle
クラスに、これらのほとんどの書式オプションをJavaプログラマが設定する際に便利な方法が提供されていることです。 たとえば、数値を含み、グリッドを使用しない列の書式を作成するには、次のようにします。
DataObject columnFormat = new TableStyle(TableStyle.HIDE_GRID_MASK |
TableStyle.NUMBER_FORMAT_MASK);
JavaScriptを使用することにより、特定のTable
操作をクライアント側で実行できます。 これらのすべての動作には、TableProxy
JavaScriptオブジェクトが必要です。 このオブジェクトを使用するには、proxied
属性をBoolean.TRUE
に設定する必要があります。
<table proxied="true">
...
</table>
次に、TableProxy
を使用して表要素を読み込む例を示します。 この例では、列のすべての値を総計し、合計を更新します。 次に、UIX XMLコードを示します。
<dataScope>
<provider>
<data name="tableData">
<inline>
<row name="Person 1" cost="11" />
<row name="Person 2" cost="12" />
<row name="Person 3" cost="13" />
<row name="Person 4" cost="14" />
...
</inline>
</data>
</provider>
<contents>
<form name="form1">
<contents>
<table proxied="true" tableData="${uix.data.tableData.row}"
name="table1" ... >
<contents>
<column>
<columnHeader>Name</columnHeader>
<contents>
<text text="${uix.current.name}"/>
</contents>
</column>
<column>
<columnHeader>Cost</columnHeader>
<contents>
<textInput name="cost" text="${uix.current.cost}"/>
</contents>
</column>
</contents>
<columnFooter>
<totalRow destination="javascript:updateTotal();">
<contents>
<textInput name="total" text="50"/>
</contents>
</totalRow>
</columnFooter>
</table>
</contents>
</form>
</contents>
</dataScope>
更新ボタンをクリックすると、updateTotal()
JavaScriptメソッドがコールされます。 このメソッドは、表のname
を使用して新規のTableProxy
を作成し、getLength()
メソッドをコールすることにより表の行数を取得します。さらに、(各行の)列のフォーム要素を取得し、値を総計して適切なテキスト・フィールドに合計を書き込みます。 getFormElement(...)
メソッドは注意して使用してください。このメソッドは、要素のname
および行索引を受け取り、その要素を返します。
function updateTotal()
{
var proxy = new TableProxy('table1');
var rowCount = proxy.getLength();
var total = 0;
for(var i=0; i<rowCount; i++)
{
var currTextField = proxy.getFormElement('cost', i);
// the minus zero here is necessary to convert the string value
// to a number
total += currTextField.value - 0;
}
document.form1.total.value = total;
}
表が単一選択モードで使用されている場合は、TableProxy
のgetSelectedRow()
メソッドを使用し、現在選択されている行の索引を取得できます。 次に例を示します。
function hire()
{
var proxy = new TableProxy('table1');
var row = proxy.getSelectedRow();
// check to make sure that something was selected
if (row < 0)
{
alert('You have not chosen anyone!');
}
else
{
var name = proxy.getFormElement('theName', row).value;
alert('You have chosen to hire '+name);
}
}
この関数は、次のUIX XMLによりコールされます。
<table name="table1" ...>
<tableSelection>
<singleSelection ...>
<contents>
<button text="Hire" destination="javascript:hire()"/>
</contents>
</singleSelection>
</tableSelection>
<contents>
<column>
<columnHeader>Name</columnHeader>
<contents>
<text text="${uix.current.name}"/>
</contents>
</column>
<column>
<columnHeader>Name</columnHeader>
<contents>
<text text="${uix.current.cost}"/>
</contents>
</column>
<formValue name="theName" value="${uix.current.name}"/>
</contents>
</table>
同様に、TableProxy
に対してgetSelectedRows()
メソッドを使用し、複数選択モードで選択された行索引を取得します。 このメソッドは配列を返します。配列の各要素は、選択された行の索引です。 次に、JavaScriptコードのサンプルを示します。
function recruit()
{
var proxy = new TableProxy('table1');
var rows = proxy.getSelectedRows();
var length = rows.length;
// make sure that something was selected
if (length > 0)
{
var list = "";
// loop through each selected row and concatenate the name
for(var i=0; i < length; i++)
{
// get the next selected row index
var row = rows[i];
// get the selected row (from the index) and pull out the name
var name = proxy.getFormElement('theName', row).value;
list += '\n'+name;
}
alert("You have chosen to recruit "+list);
}
else
{
alert("You have not chosen anyone to recruit!");
}
}
このメソッドは、次のUIX XMLからコールされます。
<table ... >
<tableSelection>
<multipleSelection text="Select ...">
<contents>
<button text="Recruit" destination="javascript:recruit()"/>
</contents>
</multipleSelection>
</tableSelection>
...
</table>
このトピックでは、TableBean
について説明し、これを使用して表形式のデータを表示する方法、およびレコード・ナビゲーション、ソート、選択、ディテール公開および合計を実装する方法を説明しました。 詳細は、TableBean
のJavadoc、またはUIX XMLの<table>
要素のリファレンスを参照してください。
Copyright © 2001, 2004, Oracle Corporation.
All rights reserved.