UIX開発者ガイド | ![]() 目次 |
![]() 前へ |
![]() 次へ |
table
は、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
では、拡張した表示および追加情報の表示に他の書式オプションを使用できますが、これらについてはこのトピック全体を通して説明していきます。それでは、表を作成しましょう。
先に進む前に、最初のuiXML例を見てみましょう。これは最も基本的な表です。
<table xmlns="http://xmlns.oracle.com/uix/ui"/>
この例は設計が十分ではなく、実際には何も表示しません。これは、UIX Componentsのネームスペースにtable要素を表示することを宣言したものです。実際にはまだ表を構成していないため、空白のように見えます。何かを表示するには、データ項目および列を追加する必要があります。これにはスタンプを使用します。
スタンプとは何でしょうか。スタンプとは、特定のページで複数回レンダリングされる単なる(任意の)UINode
です。機密書類にスタンプで印を付けるように使用されるたびに同じマークを生成するため、スタンプと呼ばれます。スタンプには、繰り返す必要のあるボタン、テキスト・フィールドまたはその他の任意の部品を指定できます。Table
では各列にスタンプが1つ存在し、このスタンプが表の列のセルすべてに対して下方向に繰り返され、レンダリングされます。表に列のスタンプを追加するには、tableに索引付けされた子を生成します。索引付けされた子はそれぞれ新規の列スタンプになります。最初の索引付けされた子が一番左の列スタンプになり、後続の子は直前のスタンプの右に追加されます。
表の列にデータをレンダリングするためにスタンプの概念を使用するのはなぜでしょうか。表は、類似するデータ項目の比較に使用されることに注意してください。各列はデータ項目の1つの内容を表すため、列のセルの外観を類似させることには意味があります。各セルのスタンプを類似させるが同一にはしない場合については、このトピックで後述します。
最初の列スタンプをtableの例に追加します。
<table ... >
<contents>
<!-- the first column stamp, a text node -->
<text text="SampleText"/>
</contents>
</table>
tableにスタンプを追加しましたが、まだ特に何も表示されません。これは、次の基本のルールによるものです。
DataObject
に対して1回レンダリングされる。 このルールは、DataObject
がない場合、スタンプはレンダリングされないことを意味します。そこで、データをいくつか追加します。
必要なDataObject
は、最初に調べる属性であるtableData
により提供されます。この属性は、表の行を表します。前述したように、表の行数はその存続期間中に変化することがあります(ユーザーがショッピング・カートの品を追加または削除した場合など)。可変長リストを伴うすべてのUIX Componentsの概念と同様に、このtableData
属性では、タイプとしてDataObjectList
が含まれています。DataObjectList
は、長さを返すことと、各DataObjectへの索引によるアクセスを可能にすることという2つの処理のみを実行することに注意してください。tableData
では、DataObjectList
の長さで表示される行数が決まり、リスト内の各DataObject
で対応する行のデータを提供します。
例にサンプル・データを配置します。これは単なるデモであるため、uiXMLのインライン・データ機能を使用します。ほとんどの実際のアプリケーションでは、より複雑な動的ソースを介してデータを公開します。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="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 data:tableData="demoRowData@demoTableData">
<contents>
<!-- the first column stamp, a text node -->
<text text="SampleText"/>
</contents>
</table>
</contents>
</dataScope>
デモのコードにいくつかの項目を追加しました。
<dataScope>
要素に配置し、表にデータソースを指定しました。表自体は<dataScope>
のコンテンツ部に移動され、データは<provider>
セクションに配置されています。
DataObjectList
にマージします。マージされたDataObjectList
は要素3つ分のサイズで、各要素はDataObjectList
の索引付けされた3つのDataObject
の1つを表します。
data: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 data:tableData="demoRowData@demoTableData">
<contents>
<!-- the first column stamp, a text node -->
<text text="SampleText"/>
<!-- the second column stamp, a button -->
<button text="Push Me" destination="http://www.example.org"/>
</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 data:tableData="demoRowData@demoTableData">
<contents>
<!-- the first column stamp, a text node -->
<text data:text="someText"/>
<!-- the second column stamp, a button -->
<button data:text="someText" destination="http://www.example.org"/>
</contents>
</table>
</contents>
</dataScope>
これで、行ごとにコンテンツの異なる表ができました。変更箇所は次のとおりです。
DataObject
それぞれにデータを1つ追加しました。このデータは、DataObject
がキーsomeTextで問合せされた場合に返されるテキスト文字列です。結果は行ごとに変えました。
DataObject
を問い合せた結果として返されるようにしました。属性値としてsomeText@aNamedDataObjectではなく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 data:tableData="demoRowData@demoTableData">
<contents>
<!-- the first column stamp, a text node -->
<text data:text="firstColumnText"/>
<!-- the second column stamp, a button -->
<button data:text="secondColumnText" destination="http://www.example.org"/>
</contents>
</table>
</contents>
</dataScope>
各行のDataObject
に、1つではなく、firstColumnTextおよびsecondColumnTextという2つのテキスト値を与えました。続いて、テキスト列のスタンプをfirstColumnText
にバインドし、ボタンをsecondColumnText
にバインドしました。その結果、2つの列が異なるテキストを持つようになります。
この例は単純ですが、考え方は非常に有用です。任意のUINode
を列スタンプとして使用することが可能で、そのスタンプの任意の属性を現行指定されているDataObject
にバインドできることに留意してください。これにより、表のセルのコンテンツを非常に柔軟に制御できます。
この時点で、いくつかの疑問が発生します。スタンプを使用し、データをわざわざ分離している理由は何でしょうか。開発者が、セルごとにコンテンツを、またはコンテンツとデータをまとめて指定できないのでしょうか。
表のモデル(またはデータ)をビュー(または外観)から分離することには相応の理由があります。分離することにより、開発者は、列スタンプを変更して表の外観を指定し、後で任意のデータソースをプラグインしてこれらのスタンプに具体的なデータを提供できます。各セルに入れるデータを厳密に指定すると、同じセットのUIX Componentsのノードをすべてのページ・ビューに使用することはできません。レンダリングごとに表の行数を変更したり、各セルのデータを変更することもできません。表の構造を一度指定し、その構造をすべてのページ・レンダリングに再利用することは、UIX Componentsフレームワーク全体のコア機能の1つです。
TableData
tableData
属性はDataObjectList
タイプであるため、いくつかの方法で設定できます。変更されない静的データは、次の例で示すようにuiXMLコードにインラインで定義できます。
<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>
uiXMLパーサーは、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" data:tableData="demoRowData@demoTableData">
...
</table>
<table name="table2" data:tableData="demoRowData@demoTableData">
...
</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 data:tableData="@demoTableData">
...
</table>
</contents>
</dataScope>
前述したtableData
バインドでの変更点に注意してください。DataObject
の内部でDataObjectList
をラップしないためです。表データを作成するJavaコードを次に示します。(データ・バインドのuiXMLおよびJavaは、どちらも「データ・バインド」のトピックを参照してください)。
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;
}
}
対応するuiXMLコードは次のようになります。
<dataScope ... >
<provider>
<data name="demoTableData">
<method class="test.MyTable" method="getDirectoryData" />
</data>
</provider>
<contents>
<table data:tableData="@demoTableData">
<contents>
<text data:text="name"/>
<text data:text="length"/>
</contents>
...
</table>
</contents>
</dataScope>
Javaでの表データの作成に役立つもう1つのクラスは、Java Vector
(およびJDK1.2以上のList
)をDataObjectList
に変換するoracle.cabo.ui.data.ListDataObjectList
です。サーバー側での表データの処理に役立つクラスは他にもあります。これらのクラスについては、後のセクションで説明します。
これまでは、表のデータ部分のみを扱い、データを取り囲む表の周辺に関する項目は扱いませんでした。ここではまず、各列にラベルを提供する列ヘッダーを調べます。次に、各行にラベルを提供する行ヘッダーを調べます。列ヘッダーを作成する方法は2つあります。ここでは最初の方法を説明し、2番目の方法は列のカプセル化に関するセクションで説明します。
列自体と同様に列ヘッダーもスタンプされますが、この場合は、列のセルを垂直方向にではなく、表の上部に水平方向にスタンプされます。スタンプ・ノードを列ヘッダーとして機能するよう登録するには、これをcolumnHeaderStamp
という名前の付けられた子として設定する必要があります。次の例でこれを示します。
<dataScope ... >
<provider>
...
</provider>
<contents>
<form name="testForm">
<contents>
<table ... >
<contents>
...
</contents>
<!-- add a column header stamp node -->
<columnHeaderStamp>
<text text="Column Header"/>
</columnHeaderStamp>
</table>
</contents>
</form>
</contents>
</dataScope>
データ列の上に、他のセルと異なる外観の特別なセクションが作成されたことに注意してください。列ヘッダー・スタンプは、データ列ごとに1回ずつ、表の上部に水平方向にスタンプされます。もちろん、すべてのセルに同じテキストを表示するのではなく、列ヘッダー・スタンプには列の内容に適したラベルを含めます。このため、表で新規の属性を使用して、columnHeaderData
という列ヘッダーをデータ・バインドする必要があります。
<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 ... />
...
<!-- DataObjectList to provide information to the column header stamps -->
<demoColumnHeaderData headerText="First Header"/>
<demoColumnHeaderData headerText="Second Header"/>
</inline>
</data>
</provider>
<contents>
<form name="testForm">
<contents>
<table data:columnHeaderData="demoColumnHeaderData@demoTableData"
... >
<contents>
...
</contents>
<!-- add a column header stamp node -->
<columnHeaderStamp>
<text data:text="headerText"/>
</columnHeaderStamp>
</table>
</contents>
</form>
</contents>
</dataScope>
これで、各ヘッダーに独自のテキストが生成されます。ここでは次の3つの作業を行いました。
DataObjectList
を作成しました。このDataObjectList
のサイズは列数と同じで、各DataObject
には、headerTextキーで格納された、列ヘッダーに表示するテキストが含まれています。
columnHeaderData
がデータ・バインド属性から取得されるよう登録しました。この属性の値は、demoTableDataという名前のDataObject
の下にあるdemoColumnHeaderDataというDataObjectList
です。これにより、表で新規のDataObjectList
を使用して、ヘッダー・スタンプにデータを提供できます。
DataObject
のheaderTextキーから取得されるようバインドしました。列ヘッダー・スタンプがレンダリングされる際、列ヘッダー・データ内の個々のDataObject
が、各ヘッダーの上にスタンプがレンダリングされるときの現行のDataObject
になります。これにより、テキストが列ヘッダーごとに変更されます。
これは、多くの表に当てはまります。ただし、一部の表では、ユーザーがなんらかの基準によりデータ項目をソートできるようにし、行が現在ソートされていることを示す必要もあります。このため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 ...
data:rowHeaderData="demoRowHeaderData@demoTableData" >
<contents>
...
</contents>
<!-- row header stamp node -->
<rowHeaderStamp>
<text data:text="headerText"/>
</rowHeaderStamp>
</table>
</contents>
</form>
</contents>
</dataScope>
行ヘッダーを追加するメカニズムは同じで、子および属性の名前のみが若干異なります。rowHeaderStamp
という名前の付けられた子およびrowHeaderData
というtableの属性を使用します。ただし、この例では、行ヘッダー・データの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>
<textInput data:text="firstColumnText" name="foo"/>
<button data:text="secondColumnText" destination="http://www.example.org"/>
</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 Controllerの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 ControllerのUIConstants.GOTO_EVENT
というイベント(uiXMLではgoto
)を生成します(イベントの詳細は、「UIX Controllerの概要」のトピックを参照してください)。このイベントには、次の表で説明する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);
private static final DataObjectList _TABLE_DATA = _sCreateTableData();
/**
* this method creates all the static table data.
*/
private static DataObjectList _sCreateTableData()
{
int sz = 94;
Object[] data = new Object[sz];
for(int i=1; i<=sz; i++)
{
data[i-1] = "Test Data "+i;
}
return new ArrayDataSet(data, "firstColumnText");
}
}
この例では、まずvalueパラメータの取得から始めています。これは、表に現在表示されている先頭行の索引です。DataObjectList
の索引はゼロから始まりますが、valueパラメータは1から始まるため、オフセット調整が必要です。
PagedDataObjectList
は、必要なブロック・サイズ(30)および現在の先頭の索引で作成されます。最後に、このDataObjectList
をDataObject
に配置します。DataObject
は、tableのvalue
属性、blockSize
属性、maxValue
属性およびtableData
属性にそれぞれバインドする必要のあるキー、value、size、maxValueおよびcurrentを実装します。これは、次のuiXMLで行います。
<table data:tableData="current@tableData@ctrl:eventResult"
data:value="value@tableData@ctrl:eventResult"
minValue="1"
data:maxValue="maxValue@tableData@ctrl:eventResult"
data:blockSize="size@tableData@ctrl:eventResult"
name="table1"
... >
...
</table>
この例では、ある程度間接的なデータ・バインドを使用しています。データ・バインドの詳細は、「データ・バインド」を参照してください。
前述した例では、すべてのナビゲーション・リンクが現在のページをポイントしています。一部のアプリケーションでは、ナビゲーション・リクエストを他のURLに向ける必要があります。これには、tableにdestination
属性を設定します。destination
には、イベント・パラメータを受け入れるサーバーのURLを整形式で指定する必要があります。宛先が指定されている場合、表はレコード・ナビゲーション・リンクをページにレンダリングし、必要なレコード・ナビゲーション・イベントとともにURLパラメータを指定の宛先に送信します。次に例を示します。
<table destination="http://www.example.org/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 data:tableData="demoRowData@demoTableData"
...
alternateText="(No search results were found)">
...
</table>
</contents>
</dataScope>
テキスト・メッセージは、tableにtableDataが設定されていない場合、またはtableDataで返される行数が0の場合にのみ表示されます。
列ヘッダーは、columnHeaderStamp
としてSortableHeaderBean
を使用することによりソート可能になります。ただし、列がソート可能であるかは、追加情報を提供するまで明らかにはなりません。
ソート可能であることを示す方法でSortableHeaderBean
がレンダリングするには、sortable
属性に値を指定する必要があります。この属性に指定できる値は3つあります。
uiXML | 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 ... />
...
<!-- DataObjectList to provide information to the column header stamps -->
<demoColumnHeaderData ... sortValue="yes"/>
<demoColumnHeaderData ... sortValue="ascending"/>
</inline>
</data>
</provider>
<contents>
<form name="testForm">
<contents>
<table ... >
<contents>
...
</contents>
<!-- add a sortable column header stamp node -->
<columnHeaderStamp>
<sortableHeader data:text="headerText"
data:sortable="sortValue"/>
</columnHeaderStamp>
</table>
</contents>
</form>
</contents>
</dataScope>
ソート可能なヘッダーをクリックすると、表はUIX ControllerのUIConstants.SORT_EVENT
というイベント(uiXMLでは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
でキーとして使用することにより、ソートが必要な列データを取得できるため、列データと同じキーを使用することには意味があります。次のuiXMLは、この部分を示しています(name、ageおよびbloodという3つのソート可能な列があります)。
<table ... >
<columnHeaderData>
<col text="Name" sort="yes" value="name"/>
<col text="Age" sort="yes" value="age"/>
<col text="Blood Group" sort="yes" value="blood"/>
<col text="Phone"/>
</columnHeaderData>
<columnHeaderStamp>
<sortableHeader data:text="text"
data:value="value" ... >
</sortableHeader>
</columnHeaderStamp>
<contents>
<text data:text="name"/>
<text data:text="age"/>
<text data:text="blood"/>
<text data:text="phone"/>
</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
を使用して値の取得を試行しますが、それに失敗した場合には、columnHeaderData
内の、この列に対する適切なDataObject
から値を取得します。これは、次のuiXMLコードにより処理されます。
<table ... >
<columnHeaderData>
<col text="Name" sort="yes" value="name"/>
<col text="Age" sort="yes" value="age"/>
<col text="Blood Group" sort="yes" value="blood"/>
<col text="Phone"/>
</columnHeaderData>
<columnHeaderStamp>
<sortableHeader data:text="text"
data:value="value">
<boundAttribute name="sortable">
<defaulting>
<!-- first try to get a value from the sortColumnHeader
dataObject. This dataObject is passed whatever the
value key is bound to (in the current columnHeaderData) -->
<dataObject data:select="value"
source="sortColumnHeader"/>
<dataObject data:source="(value)@sortColumnHeader"/>
<!-- if the above boundValue returns null, then try to get
a value from the columnHeaderData -->
<dataObject select="sort"/>
</defaulting>
</boundAttribute>
</sortableHeader>
</columnHeaderStamp>
...
</table>
ここでのboundAttribute
要素は、sortableHeader
のsortable
属性をデータ・バインドするもう1つの方法を示しています。defaulting
のboundValueでは、最初の子から値の取得を試行しますが、失敗した場合には2番目の子の値を返します。上の例では、最初の子がsortColumnHeaderデータ・プロバイダから値の取得を試行し(このデータ・プロバイダは、前述の例で作成した_SORT_COLUMN_HEADER
にバインドされていると想定)、2番目の子がsortキーを使用して、columnHeaderData
内のDataObject
からデフォルトのソート可能状態を取得します。
ここまでの処理を整理します。ソート可能なヘッダーは、クリックに対応しているだけでなく、クリックされるたびにソート表示の昇順と降順を切り換えます。ただし、列はまだソートされません。次のセクションでは、実際のソート処理を実装します。
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" ctrl:event="copy" />
<submitButton text="Delete" ctrl: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" ctrl:event="copy"/>
<submitButton text="Delete" ctrl:event="delete" />
</contents>
</multipleSelection>
</tableSelection>
...
</table>
multipleSelection
のselected
属性およびselection
属性をデータ・バインドすることにより、初期選択項目を設定できます。MultipleSelectionBean
が列にスタンプされるたびに、そのselected
属性が評価されます。このときに、selection DataObjectList
の対応する行が現行のDataObject
になります。
<multipleSelection data:selected="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"/>
<row name="Person 2" age="12" disabledKey="false"/>
<row name="Person 3" age="13" disabledKey="false"/>
<row name="Person 4" age="14" disabledKey="true"/>
</inline>
</data>
</provider>
<contents>
<form name="form1">
<contents>
<table data:tableData="row@tableData" ... >
<tableSelection>
<multipleSelection ...
data:disabled="disabledKey">
</multipleSelection>
</tableSelection>
...
</table>
</contents>
</form>
</contents>
</dataScope>
一部のアプリケーションでは、表の行に特定要素のプロパティの概要のみが表示されます。このような場合、ボタンをクリックすることで、その行の詳細情報が表示されるようにすると便利です。TableBean
のディテール公開機能により、この機能が提供されます。
ディテール公開は、table
のdetail
という名前の付けられた子を設定することによりオンになります。この子は、ユーザーが行の詳細を要求したときのみレンダリングされる点を除き、他のスタンプと同様です。したがって、この子は現在の行の詳細をすべて表示できます。次に例を示します。
<table ... >
<detail>
<!-- this is the detailed stamp -->
<labeledFieldLayout>
<contents>
Name
<styledText data:text="name" styleClass="OraDataText"/>
Age
<styledText data:text="age" styleClass="OraDataText"/>
Blood Group
<styledText data:text="blood" styleClass="OraDataText"/>
Phone
<styledText data:text="phone" styleClass="OraDataText"/>
</contents>
</labeledFieldLayout>
</detail>
<columnHeaderData>
<col text="Name"/>
<col text="Age" />
</columnHeaderData>
<contents>
<!-- these are the regular column stamps -->
<text data:text="name"/>
<text data:text="age"/>
</contents>
</table>
この例では、通常の表に各行のNameプロパティおよびAgeプロパティのみ表示されます。ユーザーが詳細を要求すると、detail
スタンプがレンダリングされ、Blood GroupやPhoneなどの追加情報が提供されます(detail
という子は、現行のDataObject
として表の現在の行であるDataObject
でレンダリングされます。そのため、データ・バインドは、列スタンプと同じ方法で動作します)。
ただし、この例では、詳細セクションが公開されていません。このため、レンダリングされたdetail
スタンプを参照できませんでした。UIとしてレンダリングされる各矢印の公開状態は、table
のdetailDisclosure
属性により制御されます。これは、表の各行に対応するDataObject
を持つDataObjectList
である必要があります。各DataObject
は、キーUIConstants.DISCLOSED_KEY
(または、uiXMLでは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 Controllerのイベント(uiXMLでは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
に挿入されるため、uiXMLコードからアクセスできます。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
はその長さとしてゼロを返します。したがって、この特殊なケースは慎重に処理する必要があります。
場合によっては、データ領域の下部にある特別なセクションに、表のデータを要約して入れると適切な場合があります。これは列フッターと呼ばれ、表の名前の付けられた子でもあります。ただし、列ヘッダー・スタンプや行ヘッダー・スタンプとは異なり、列フッターはスタンプではありません。列フッターは、すべての列にスタンプされるのではなく、表の一番下に1回のみレンダリングされます。ほとんどの列フッターは、列ごとの処理ではなく、表全体の統計や処理を要約するように設計されています。列フッターとしてのみ使用される特殊なUINode
には、addTableRow
およびtotalRow
の2つがあります。この2つについて詳しく説明します。
AddTableRow
Beanまず、addTableRow
です。この列フッター・ノードは、ユーザーが実行時にデータ行を追加できるようにする表で使用するためのものです。追加する行数や表示するテキストのカスタマイズ、URLリンク先の変更を行うことができます。ただし、指定されていない場合はすべてデフォルトになります。次に、使用例を示します。
<table ... >
...
<!-- column footer node -->
<columnFooter>
<addTableRow rows="5"/>
</columnFooter>
</table>
この例では、ユーザーは5行を追加するよう求められますが、そのrows
属性がない場合は、Beanがアクティブになったときに新しい1行が要求されます。また、これは、イベントをサーバーに送信するよう要求する表のカスタマイズです。ただし、これだけでは行は実行時に追加されません。ページは、追加の行を含めて再生成される必要があります。つまり、サーバーが通知を受けて、データソースを更新する必要があります。次に、UIConstants.ADD_ROWS_EVENT
(uiXMLではaddRows)イベントの発生時にサーバーに送信されるパラメータを示します(これらは、これまでに説明したパラメータと似ています)。
パラメータ | UIConstant | 説明 |
---|---|---|
source | SOURCE_PARAM
| 表の名前 |
size | SIZE_PARAM
| 追加するよう要求された行数 |
TotalRow
Bean前述のBeanと似ているのはtotalRow
です。行を追加するかわりに、このBeanを使用して表の数値列の合計を表示します。ただし、これだけではBean自体は合計を計算しません。サーバーまたはJavaScriptにより計算された合計を適切に書式設定されたコントロールに表示します。
totalRow
には、ユーザーが合計を更新できるよう列フッターにボタンをレンダリングし、また、各列にレンダリングされるコントロールがその列の合計値を保持できるようにするという2つの役割があります。ボタンは、Beanのtext
属性を使用して構成できるテキストとともに自動的にレンダリングされます。ボタンを再びアクティブにすると、イベントがサーバーに送信されます。このときのイベントはUIConstants.UPDATE_EVENT
(uiXMLではupdate)です。
パラメータ | UIConstant | 説明 |
---|---|---|
source | SOURCE_PARAM
| 表の名前 |
addTableRow
とは異なり、totalRow
では、索引付けされた子を使用でき、それによって各列の合計値を保持できます。totalRow
の最初の索引付けされた子は、表の一番右の列に配置され、後続の索引付けされた子は前の列の左側に配置されます。このため、次の例で示すように、索引付けされた子は、追加されると右から左の順で各列に表示されます。
<table ... >
...
<!-- column footer node -->
<columnFooter>
<totalRow>
<contents>
<textInput name="total" text="42" columns="5"/>
</contents>
</totalRow>
</columnFooter>
</table>
ここでも、更新ボタンがアクティブな場合に、合計を表す索引付けされた子ノードに対する実際の更新は、表の作成者が実行する必要があります。
場合によっては、表にtotalRow
およびaddTableRow
の両方が必要です。これは、次の例で示すように、addTableRow
の索引付けされた子としてtotalRow
を追加することにより実行できます。
<table ... >
...
<columnFooter>
<addTableRow>
<contents>
<!-- addTableRow beans may have at most a single child,
and this child can only be a totalRow bean -->
<totalRow>
<contents>
<textInput name="total" text="400" />
</contents>
</totalRow>
</contents>
</addTableRow>
</columnFooter>
</table>
これまでは、表のセルの実際のコンテンツ、およびサーバーと通信するいくつかの処理についてのみ扱ってきました。データの構造を明確にするため、または読みやすくするために、表の外観または書式を変更することが必要な場合があります。表の書式をカスタマイズする方法は多数あり、その組合せの数も膨大になります。幸い、これらの異なるカスタマイズを実行するメカニズムには一貫性があります。
表の外観を変更する最も簡単な方法は、表の幅を変更することです。表の幅は、表のwidth
属性を設定するだけで変更できます。幅は、ピクセル単位または親要素の幅に対するパーセントで指定できますが、一般にはパーセントが好まれます。ただし、表の幅に指定した値が、コンテンツを十分に収容できる大きさでない場合は、表示の際に上書きされ、必要な最低容量が使用されます。
次に、コンテンツのセルの位置揃えについて説明します。ユーザー・インタフェースの設計に応じて、タイプの異なるコンテンツの配置方法を区別し、ユーザーが理解しやすいようにする必要があります。これは、書式で使用されるDataObject
の新規セットを使用して行うことができます。この場合、位置揃えは特定の列のセル全体で統一されている必要があるため、表のcolumnFormat
属性を使用します。他の多くのデータ・ドリブン表属性と同様に、これもDataObjectList
です。この場合は、リスト内の各DataObject
が、表レイアウトの左から右の順で列の1つに対応します。これらのDataObject
のいずれかで書式を指定すると、対応する列の外観がそれに応じて変更されます。次に最初の例を示します。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
...
<!-- DataObjectList to provide column formatting -->
<demoColumnFormats/>
<demoColumnFormats columnDataFormat="numberFormat"/>
<demoColumnFormats columnDataFormat="iconButtonFormat"/>
</inline>
</data>
</provider>
<contents>
<form ... >
<contents>
<table ...
width="100%"
data:columnFormats="demoColumnFormats@demoTableData">
<contents>
<!-- the first column stamp, a text node -->
<textInput data:text="firstColumnText" name="foo"/>
<!-- a second column stamp, a static text node -->
<text text="42"/>
<!-- the third column stamp, a button -->
<button data:text="secondColumnText" destination="http://www.example.org"/>
</contents>
...
</table>
</contents>
</form>
</contents>
</dataScope>
この例ではいくつかの項目を変更しました。順番に説明します。表の幅は100%に設定しました。続いて、表に別の列スタンプ(テキスト・ノード)を追加して、処理内容を増やしました。次に、前と同様に新しいDataObjectList
を作成し、今回はdemoColumnFormatsという名前を付けました。このリスト内のDataObject
は、3つの各列の書式設定を行います。これらのDataObject
に、書式データを挿入しました。詳細は後述します。最後に、表自体に新規属性columnFormats
を設定しました。この属性は、新規のDataObjectList
をポイントします。
表の書式に使用されるDataObject
をさらに詳しく説明します。書式情報を提供することはすでに示しましたが、これがどのように機能するのかについて説明します。実装の実行者が選択した任意のキーで値を格納する他のDataObject
とは異なり、書式オブジェクトは、既知の公開されたキーで値を格納する必要があります。レンダリングの際に、表はこれらの各列書式DataObject
に、これらの各パブリック・キーに格納された値を要求します。DataObject
がキーの値を返した場合は、その値が列書式の変更に使用されます。キーの値が返されなかった場合は、デフォルトの書式が使用されます。
この例では、書式DataObject
は、既知のキーの1つであるcolumnDataFormat文字列で値を格納しました。このキーは、宣言されているコンテンツのタイプに基づいて、列のコンテンツの位置揃えを決定します。最初のDataObject
は、キーの値を返しません。そのため、テキストのコンテンツの位置揃えはデフォルトの左揃えになります。2番目のDataObject
は、問合せされた際に値numberFormatを返します。仕様では、数値は右揃えにするよう規定されているため、この表の列のデータ・セルはそれに従って配置されます。最後に、3番目の列はそのデータ型としてiconButtonFormat
を返します。現在、この型はコンテンツを中央揃えにします。中央揃えは、アイコンまたはボタンの列に対して適切な位置揃えです。これは、前の例を参照するとよくわかります。
列書式DataObject
は、4つのキーの値を返すことができます。このすべての例を次に示します。
<dataScope ... >
<provider>
<!-- all the data used by our table demo -->
<data name="demoTableData">
<inline>
...
<!-- DataObjectList to provide column formatting -->
<demoColumnFormats cellNoWrapFormat="true"/>
<demoColumnFormats columnDataFormat="numberFormat"
displayGrid="false"/>
<demoColumnFormats columnDataFormat="iconButtonFormat"
width="100%"/>
</inline>
</data>
</provider>
<contents>
...
</contents>
</dataScope>
この例に基づき、列書式DataObject
で可能なすべての書式についてまとめると、次のようになります。
キー UIConstant |
説明 |
---|---|
columnDataFormatCOLUMN_DATA_FORMAT_KEY
| 表自体にはコンテンツのタイプを認識する機能がないため、このキーを使用して、表の列に表示されるコンテンツのタイプを指定します。異なるデータ書式を指定すると、データ・セルの位置揃えが変更されます。
|
cellNoWrapFormatCELL_NO_WRAP_FORMAT_KEY
| セルのコンテンツは、複数の行にまたがって表示しないほうがよい場合もあります。折返しをしないほうが適切なデータ列については、このキーが問合せされた際に、列書式DataObject でtrue(Boolean.TRUE )を返すようにします。折返しを禁止すると表の幅が広くなりすぎることがあるため、これはデフォルトの動作ではありません。
|
widthWIDTH_KEY
| Used to indicate what percentage of extra width in the table this particular column should take.表にコンテンツで必要とされる以上の領域がある場合は、その領域を列に配分します。ただし、一部の列(ボタンなど)には、与えられた最小の領域よりも多くの領域は必要ありません。テキスト列などのその他の列で、コンテンツの表示に必要な行を削減できる場合は、追加の領域を使用するほうが理想的です。列書式DataObject のとる値は、50%や100%などのパーセント値で、列が取得する追加領域の割合を示します。すべての列の合計は100%になる必要があります。
|
displayGridDISPLAY_GRID_KEY
| デフォルトでは、グリッド線はすべての列の左に表示されます。場合によっては、垂直のグリッド線の数を減らし、他の列間の関係と比較して一部の列間の関係を強調するようにする場合もあります。特定のデータ列の前、つまり左側の垂直グリッド線をオフにする場合は、その列がこのキーで問合せされた際にfalseを返します。 |
これらすべての列書式の値は厳格なルールではなく、ヒントにすぎません。一部のレンダリングの外観やプラットフォームでは、すべての書式の値がサポートされているとはかぎりません、あるエージェントで特定のルールが通用しない場合もあります。書式は依存性に欠けるため、アプリケーションにとって重要ではありません。
垂直グリッド線の表示場所を列で制御できることと同様に、行の水平グリッド線の表示場所をカスタマイズすることもできます。このカスタマイズは、次の例で示すように、表の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 ...
data:rowFormats="demoRowFormats@demoTableData">
...
</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 ...
data:tableFormat="demoTableFormat@demoTableData">
...
</table>
</contents>
</form>
</contents>
</dataScope>
同様に、次の例で示すように、値rowBanding(UIConstants.ROW_BANDING
)を返すと、表の行の背景色が変更されます。bandingInterval(UIConstants.BANDING_INTERVAL_KEY
)キーで問合せされた際に整数を返して、変更される各バンドの行数を制御することもできます。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="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>
ただし、列バンドは任意のパターンでも実行できます。これにより、個々の列を、通常の変更パターンを使用せずに明るい色にすることができます。このためには、bandingShade(UIconstants.BANDING_SHADE_KEY
)キーで問合せされた際に、列書式DataObject
(前述)の1つ以上でlight(BANDING_SHADE_LIGHT
)またはdark(BANDING_SHADE_DARK
)を返すようにします。次の例では、第3列のみが明るいバンド色で表示されます。
<dataScope ... >
<provider>
<data name="demoTableData">
<inline>
...
<!-- DataObjectList to provide column formatting -->
<demoColumnFormats cellNoWrapFormat="true"/>
<demoColumnFormats columnDataFormat="numberFormat"
displayGrid="false"/>
<demoColumnFormats columnDataFormat="iconButtonFormat"
width="100%"
bandingShade="light"/>
<!-- 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>
...
</contents>
</dataScope>
任意の列書式オブジェクトに対してバンドが明示的に設定されている場合は、表全体の書式が完全に無視されるため注意してください。表全体のバンドを明示的な列バンドと組み合せることはできません。組み合せると、正常な配色パターンが生成されません。
TableStyle
クラスoracle.cabo.ui.beans.table.TableStyle
クラスに、これらのほとんどの書式オプションをJavaプログラマが設定する際に便利な方法が提供されていることです。たとえば、数値を含み、グリッドを使用しない列の書式を作成するには、次のようにします。
DataObject columnFormat = new TableStyle(TableStyle.HIDE_GRID_MASK |
TableStyle.NUMBER_FORMAT_MASK);
前のセクションでは、列スタンプの設定、columnHeaderStamp
の設定、columnHeaderData
のバインドおよびcolumnFormats
の設定について説明しました。これらの各プロパティは、表のグローバル属性の設定に関連しています。(1つの列に関する)すべてのプロパティを1つの場所で設定する方法があると便利です。この際、ColumnBean
が役立ちます。次に、使用例を示します。
<table ... >
<contents>
...
<column>
<columnFormat columnDataFormat="iconButtonFormat"
bandingShade="light" width="50%" displayGrid="false"/>
<columnHeader>Third Column</columnHeader>
<contents>
<!-- This is the column stamp -->
<button data:text="secondColumnText"/>
</contents>
</column>
</contents>
</table>
最初に注目すべき点は、column
要素が表の列スタンプとして設定されていることです。この要素は、この列のヘッダーのスタンプとして使用されるcolumnHeader
という名前を付けられた子(table
のcolumnHeaderStamp
に似ています)をサポートします。columnHeaderData
属性は、スタンプをデータ・バインドする必要がある場合にサポートされます。columnFormat
およびcolumnHeaderFormat
は、どちらも列を書式設定するためにサポートされています。
(前の例で示したように)column
要素が表の他の属性と調和して動作することにも注意する必要があります。ただし、column
で設定された属性は、table
の対応する属性を上書きします。
JavaScriptを使用することにより、特定のTable
操作をクライアント側で実行できます。これらのすべての動作には、TableProxy
JavaScriptオブジェクトが必要です。このオブジェクトを使用するには、proxied
属性をBoolean.TRUE
に設定する必要があります。
<table proxied="true">
...
</table>
次に、TableProxy
を使用して表要素を読み込む例を示します。この例では、列のすべての値を総計し、合計を更新します。次に、uiXMLコードを示します。
<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" data:tableData="row@tableData"
name="table1" ... >
<contents>
<text data:text="name"/>
<textInput name="cost" data:text="cost"/>
</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);
}
}
この関数は、次のuiXMLによりコールされます。
<table name="table1" ...>
<tableSelection>
<singleSelection ...>
<contents>
<button text="Hire" destination="javascript:hire()"/>
</contents>
</singleSelection>
</tableSelection>
<contents>
<text data:text="name"/>
<text data:text="cost"/>
<formValue name="theName" data:value="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!");
}
}
このメソッドは、次のuiXMLからコールされます。
<table ... >
<tableSelection>
<multipleSelection text="Select ...">
<contents>
<button text="Recruit" destination="javascript:recruit()"/>
</contents>
</multipleSelection>
</tableSelection>
...
</table>
このトピックでは、TableBean
について説明し、これを使用して表形式のデータを表示する方法、およびレコード・ナビゲーション、ソート、選択、ディテール公開および合計を実装する方法を説明しました。詳細は、TableBean
のJavadoc、またはuiXMLの<table>
要素のリファレンスを参照してください。