Oracle ADF UIX開発者ガイド | ![]() 目次 |
![]() 前へ |
![]() 次へ |
このトピックでは、Oracle ADF UIXのtable
の例を示します。 実装するtable
機能には、レコード・ナビゲーション、ソート、複数選択およびディテール公開が含まれます。 この例で使用するUIX XMLおよびJavaコードは、開発環境にコピーした後で簡単に変更できるように設計されています。そのため、このコードを基にして、実用的なコードをすぐに生成できます。 このトピックを読む前に、次の内容について理解しておく必要があります。
HttpSession
ここでは、次の項目について説明します。
このデモの表データは、本のリストです。 それぞれの本には、タイトル、説明、著者、ページ数、ISBN、発行元および出版日の各プロパティがあります。 そのため、本に対して次のBeanを使用します。
public class Book
{
public Book(String title, String description, String author,
String isbn, int pages, Date published, String publisher)
{
_title = title;
_desc = description;
_author = author;
_isbn = isbn;
_pages = pages;
_published = published;
_publisher = publisher;
}
public String getTitle()
{
return _title;
}
public String getDescription()
{
return _desc;
}
public String getAuthor()
{
return _author;
}
public String getISBN()
{
return _isbn;
}
public int getPages()
{
return _pages;
}
public Date getPublishedDate()
{
return _published;
}
public String getPublisher()
{
return _publisher;
}
private final String _title, _desc, _author, _isbn, _publisher;
private final int _pages;
private final Date _published;
}
このような本のライブラリが存在するのが理想的です。ユーザーは、ライブラリに接続して検索を実行します。 検索条件に一致する本は本のリストにコピーされ、ユーザーは表を使用してそのリストを参照します。
このデモでは検索は実装しません。 かわりに、リストに本をランダムに移入して、ユーザーに対して表示します。 このランダムな本のリストは、クラスoracle.cabo.doc.demo.table.BookUtils
によって作成されます。ただし、このクラスの実際の機能については、ここでは示しません。
Book
のList
は、oracle.cabo.doc.demo.table.UserData
オブジェクトに格納されます。 通常、このオブジェクトには、ユーザーの名前や作業環境などが格納されますが、このデモでは、格納されるプロパティは本のリストのみです。 UserData
オブジェクトはHttpSession
に格納されるため、本のリストはユーザーごとに存在します。
このList
が<table>
要素のtableData
属性にデータ・バインドされると、<column>
要素を使用して、Book
の各プロパティを表の列に表示できるようになります。 次の例では、Published Dateプロパティが列に表示されます。
<table name="books"
tableData="${sessionScope.userData.bookList}">
<contents>
<column>
<columnHeader>
<sortableHeader text="Published Date"/>
</columnHeader>
<contents>
<!-- standard Java introspection calls the method
getPublishedDate() to resolve the property
publishedDate -->
<text text="${uix.current.publishedDate}"/>
</contents>
</column>
<!-- Other columns -->
...
</contents>
</table>
publishedDate
キーをBeanのgetPublishedDate()
メソッドにマップする方法の詳細は、「ADF UIXでのデータ・バインディング」のトピックの関連するセクションを参照してください。
Book
のプロパティを表示するには複数の<column>
要素が必要なため、列テンプレート<demo:sortableColumn>
を作成する必要があります。 <column>
から継承される属性および子に加えて、テンプレートでは、属性header
およびkey
が認識されます。 header
のテキスト(「Published Date」など)は列のヘッダーに表示され、key
(「publishedDate」など)はBeanのプロパティを取得するために使用されます。 次に、テンプレートのUIX XMLコードを示します。
<templateDefinition targetNamespace="http://xmlns.oracle.com/uix/demo"
localName="sortableColumn">
<type base="data:column">
<attribute name="key" javaType="string"/>
<attribute name="header" javaType="string"/>
<!-- by default this column will be sortable.
However, if this attribute is set to 'false',
then this column will not be sortable -->
<attribute name="sortable" javaType="boolean"/>
</type>
<content>
<column>
<attributeMap><rootAttributeMap/></attributeMap>
<columnHeader>
<sortableHeader text="${uix.rootAttr.header}"
value="${uix.rootAttr.key}">
...
</sortableHeader>
</columnHeader>
<contents>
<text text="${uix.current[uix.rootAttr.key]}"/>
</contents>
</column>
</content>
</templateDefinition>
この新しいテンプレートを使用すると、表は次のようになります。
<table name="books"
tableData="${sessionScope.userData.bookList}">
<contents>
<demo:sortableColumn key="title" header="Title"/>
<demo:sortableColumn key="author" header="Author">
<columnFormat width="20%"/>
</demo:sortableColumn>
<demo:sortableColumn key="pages" header="Pages">
<columnFormat width="1%" columnDataFormat="numberFormat"/>
</demo:sortableColumn>
</contents>
</table>
この例では、それぞれのBook
のタイトル、著者およびページ数の各プロパティに対応する3つの列を表示します。
表にBook
を表示したので、次はレコード・ナビゲーション、ソートおよびディテール公開を実装します。 これらの機能は、クラスoracle.cabo.doc.demo.table.TableState
によって実装します。 このクラスのインスタンスは、List
(Book List
など)およびblockSize
(1ページに表示するレコードの最大数)を使用して構築されます。 このクラスには、一度に1ページずつ表のデータを取得するメソッド、行のディテール公開状態を切り替えるメソッド、およびBeanプロパティによってList
をソートするメソッドが含まれています。
レコード・ナビゲーションを実装するために、<table>
要素のblockSize
、value
およびmaxValue
を、TableState
のblockSize
、startIndex
およびlastIndex
にデータ・バインディングします。 また、<table>
要素のtableData
属性を、TableState
のcurrentRecordSet
プロパティにバインドする必要があります。 このプロパティは、各blockSize
要素のページ数のデータを返します。
Book List
を直接返すかわりに、getBookTableState()
メソッドを使用して、対応するTableState
オブジェクトを返すようにUserData
クラスを変更します。 この結果、<table>
要素は次のようになります。
<table tableData="${sessionScope.userData.bookTableState.currentRecordSet}"
blockSize="${sessionScope.userData.bookTableState.blockSize}"
maxValue="${sessionScope.userData.bookTableState.lastIndex}"
value="${sessionScope.userData.bookTableState.startIndex}">
...
</table>
この表では、ナビゲーション・コントロールをクリックすると、UIXサーブレットのgoto
イベントが起動されます。 このイベントには、表示する新しいレコード・セットの索引を示すvalue
パラメータがあります。 次のコードは、TableState
に新しいレコード・セットの索引を設定して、このイベントを処理します。
UserData uData = _getUserData(..); // from HttpSession
TableState tState = uData.getBookTableState();
String valueParam = event.getParameter(UIConstants.VALUE_PARAM);
int value = Integer.parseInt(valueParam);
tState.setStartIndex(value);
表にソート可能なヘッダーを実装するために、<sortableHeader>
要素を使用します。 TableState
オブジェクトには、sortBy
プロパティがあります。このプロパティは、表の現在のソート条件(author
など)を示す(Book
の)Beanプロパティです。 また、このオブジェクトには、ソートの方法(昇順または降順)を示すsortOrder
プロパティがあります。 これら2つのプロパティはロジックを使用して組み合され、各<sortableHeader>
要素のsortable
属性に対して値ascending
またはdescending
が生成されます。 次に、(<demo:sortableColumn>
の)関連するUIX XMLを示します。
<sortableHeader text="${uix.rootAttr.header}"
value="${uix.rootAttr.key}">
<boundAttribute name="sortable">
<if>
<!-- is this table currently sorted by the bean property displayed by
this column? -->
<comparison type="equals">
<dataObject select="sortBy">
<contextProperty select="demo:tableState"/>
</dataObject>
<dataObject source="${uix.rootAttr.key}""/>
</comparison>
<!-- if so, then we need to return either "ascending" or "descending"
depending on the sortOrder -->
<if>
<dataObject select="sortOrder">
<contextProperty select="demo:tableState"/>
</dataObject>
<fixed>ascending</fixed>
<fixed>descending</fixed>
</if>
<!-- Otherwise, we might be sortable (though not sorted in any
particular order) as long as the user has not set the sortable
attribute to false -->
<if>
<defaulting>
<dataObject source="${uix.rootAttr.sortable}"/>
<!-- by default, we are sortable -->
<fixed text="true"/>
</defaulting>
<fixed>yes</fixed>
<fixed>no</fixed>
</if>
</if>
</boundAttribute>
</sortableHeader>
ソート可能な列ヘッダーをクリックすると、sort
イベントが生成されます。 各イベントには、列に表示されているBeanプロパティを示すvalue
パラメータがあります。 このイベントの処理では、そのBeanプロパティに対してComparator
が作成され、TableState
に対してsetSortBy
メソッドがコールされます。
UserData uData = _getUserData(..); // from HttpSession
TableState tState = uData.getBookTableState();
String valueParam = event.getParameter(UIConstants.VALUE_PARAM);
// create a Comparator that will sort our list of Book beans by the
// required property:
BeanComparator sorter = new BeanComparator(Book.class, valueParam);
tState.setSortBy(valueParam, sorter);
TableState
オブジェクトには、表のディテール公開状態を保持する、detailDisclosureList
というプロパティがあります。 このプロパティは、<table>
要素のdetailDisclosure
属性にデータ・バインドする必要があります。
<table tableData="${sessionScope.userData.bookTableState.currentRecordSet}"
detailDisclosure="${sessionScope.userData.tableState.detailDisclosureList}"
blockSize="${sessionScope.userData.bookTableState.blockSize}"
maxValue="${sessionScope.userData.bookTableState.lastIndex}"
value="${sessionScope.userData.bookTableState.startIndex}">
...
</table>
(詳細列内の)表示/非表示リンクがクリックされると、UIXサーブレットのイベントhide
およびshow
が起動されます。 これらのイベントにも、クリックされた行の索引を示すvalue
パラメータがあります。 このイベントの処理では、TableState
に対してtoggleDisclosed
メソッドがコールされます。
UserData uData = _getUserData(..); // from HttpSession
TableState tState = uData.getBookTableState();
String eventName = event.getName();
String valueParam = event.getParameter(UIConstants.VALUE_PARAM);
// This handles detail disclosure. The event must have a "value"
// parameter which is the index of the row to disclose or
// undisclose. This index is based at zero. The "value" might also be
// VALUE_SHOW_ALL which signals a show-all or hide-all.
if (UIConstants.VALUE_SHOW_ALL.equals(valueParam))
{
// if the event is "show" then do a show-all. Otherwise, do a hide-all:
tState.showAllDetails(UIConstants.SHOW_EVENT.equals(eventName));
}
else
{
int value = Integer.parseInt(valueParam);
tState.toggleDisclosed(value);
}
このデモでは、前述の(レコード・ナビゲーション、ソートおよびディテール公開用の)すべてのイベントを、oracle.cabo.doc.demo.table.BookEvents
クラスのbooks_tableEvent
メソッドで処理します。 このメソッドは、source
パラメータを確認し、イベントを生成した表を識別して(このデモでは表は1つのみです)、対応するTableState
オブジェクトを取得して前述のイベント処理を実行します。 イベント・ハンドラは、UIX XMLに次のように登録します。
<event name="goto sort show hide" source="books">
<method class="oracle.cabo.doc.demo.table.BookEvents"
method="books_tableEvent"/>
</event>
同じページで複数の表をサポートすることは簡単です。 各表に個別のTableState
オブジェクトが存在する必要があります。 (表イベント用の)イベント・ハンドラでは、source
パラメータを確認することによって、使用する適切なTableState
を判断できます。
UIX XMLでは、各<table>
の属性tableData
、detailDisclosure
、blockSize
、maxValue
およびvalue
が、対応するTableState
の適切なプロパティにデータ・バインドされている必要があります。 たとえば、booksという<table>
で、UserData
に対してgetBookTableState()
メソッドをコールすることによって、TableState
を取得します。
<table name="books"
tableData="${sessionScope.userData.bookTableState.currentRecordSet}"
detailDisclosure="${sessionScope.userData.tableState.detailDisclosureList}"
blockSize="${sessionScope.userData.bookTableState.blockSize}"
maxValue="${sessionScope.userData.bookTableState.lastIndex}"
value="${sessionScope.userData.bookTableState.startIndex}">
...
</table>
すべての表に対してこのようなデータ・バインディングを実行する必要があるため、この方法は非効率的です。 より効率的に実装するには、テンプレート<demo:table>
を使用します。 このテンプレートは、<table>
のすべてのプロパティおよび子を継承しますが、tableState
属性も使用します。
<templateDefinition targetNamespace="http://xmlns.oracle.com/uix/demo"
localName="table">
<type base="data:table">
<attribute name="tableState"
javaType="oracle.cabo.doc.demo.table.TableState"/>
</type>
<content>
<table tableData="${uix.rootAttr.tableState.currentRecordSet}"
detailDisclosure="${uix.rootAttr.tableState.detailDisclosureList}"
blockSize="${uix.rootAttr.tableState.blockSize}"
maxValue="${uix.rootAttr.tableState.lastIndex}"
value="${uix.rootAttr.tableState.startIndex}">
<childList><rootChildList/></childList>
<attributeMap><rootAttributeMap/></attributeMap>
<childMap><rootChildMap/></childMap>
</table>
</content>
</templateDefinition>
これによって、開発者は、次のように記述してbooks表を作成できます。
<demo:table name="books"
tableState="${sessionScope.userData.bookTableState}">
...
</demo:table>
tableData
、detailDisclosure
、blockSize
、maxValue
およびvalue
を個別にデータ・バインドする必要がないことに注意してください。この処理は、テンプレートによって行われます。 そのため、UserData
のwishListTableState
プロパティからデータを取得するwishListという別の表を作成するには、別の<demo:table>
を作成します。
<demo:table name="wishList"
tableState="${sessionScope.userData.wishListTableState}">
...
</demo:table>
選択は、<table>
要素に子<tableSelection>
を設定すると、有効になります。 このデモでは、ユーザーの本のリストから本を削除する機能をサポートします。 そのため、クリックするとサーバーにdelete
イベントが送信されるボタンが、表のコントロール・バーに必要です。
<demo:table name="books" ... >
<tableSelection>
<multipleSelection text="Select and ...">
<contents>
<submitButton text="Delete" event="delete"/>
</contents>
</multipleSelection>
</tableSelection>
...
</demo:table>
選択に関連するイベントのイベント・ハンドラでは、oracle.cabo.servlet.ui.data.PageEventFlattenedDataSet
およびoracle.cabo.ui.beans.table.SelectionUtils
を使用して、現在選択されている索引を取得できます。 次に、前述のdelete
イベントのイベント・ハンドラに関連するコード部分を示します。
// the data that is coming from the client are form elements inside a
// table. therefore, we can use some FlattenedDataSet implementation to
// get at the data:
PageEventFlattenedDataSet clientData =
new PageEventFlattenedDataSet(event, "books");
// get the indices that were selected (note that these indices are
// relative to the current record set):
int[] selection = SelectionUtils.getSelectedIndices(clientData);
これによって、変更するTableState
(この場合はBook表の状態)から表データを取得して、削除を実行できます。
UserData uData = _getUserData(..); // from HttpSession
TableState tState = uData.getBookTableState();
List tableData = tState.getTableData();
// this is the offset to add to convert a selection index into an actual
// book index. Note that the -1 is significant since getStartIndex is
// based at 1 and the selection indices are based at zero:
int currentRecordOffset = tState.getStartIndex() - 1;
// BEGIN handle select-all
// ** select-all not yet implemented **
// END handle select-all
// if we loop forward then the books we delete first will affect the
// indices of the books we delete second. So must loop backwards:
for(int i=selection.length-1; i>=0; i--)
{
// each selected index is relative to the current record set. therefore
// we need to add an offset to get the index of the actual book:
tableData.remove(selection[i] + currentRecordOffset);
}
「すべて選択」がクリックされた場合に、(現在のレコード・セット内のレコードのみでなく)表のすべてのレコードが削除されるようにします。 この機能をサポートするために、ユーザーが「すべて選択」を使用したかどうかを検出する必要があります。 これを行うには、表のFlattenedDataSet
からUIConstants.SELET_MODE_KEY
の値を取得して、値が"all"かどうかを確認します。 (前述のコードを更新した)次のコード部分では、「すべて選択」および削除を順に処理します。
// BEGIN handle select-all
// check to see if user has picked select-all:
Object selectMode = clientData.selectValue(null /*renderingContext*/,
UIConstants.SELECT_MODE_KEY);
if ("all".equals(selectMode))
{
// we need to handle select-all+delete carefully - we can't just delete
// everything since the user might have unselected something in the
// current record set. so first, delete everything upto (but not
// including) the current record set:
tableData.subList(0, currentRecordOffset).clear();
// now the current record set starts at index 0:
currentRecordOffset = 0;
// second, delete everything that follows the current record set:
int sz = tableData.size();
int firstIndexOnNextPage = tState.getBlockSize();
// check to make sure that there are records on the next page:
if (firstIndexOnNextPage < sz) tableData.subList(firstIndexOnNextPage, sz).clear();
// third, continue deleting the selected records in the current record
// set:
}
// END handle select-all
表データは、問合せに応じてJavaコードで作成するのが理想的です。 ただし、UIモックアップを使用している場合、表データを静的インライン・データとしてUIX XMLに定義すると便利です。 このセクションでは、インライン・データを使用して対話型の表を作成する方法を示します。
これを行うには、クラスoracle.cabo.doc.demo.table.MockupUtils
の適切なメソッドを使用して、TableState
オブジェクトを作成します。 次に、簡単なUIX XMLコード部分を示します。
<dataScope>
<provider>
<data name="tableStates">
<method class="oracle.cabo.doc.demo.table.MockupUtils"
method="createTableState"/>
</data>
<!-- create some static data for the Roster table -->
<data name="Roster">
<inline>
<row subject="Math" students="33" teacher="Jacobs"/>
<row subject="Physics" students="34" teacher="Kim"/>
<row subject="Chemistry" students="31" teacher="Dutta"/>
<row subject="Biology" students="25" teacher="Liang"/>
<row subject="Phys Ed" students="50" teacher="Goldstein"/>
<row subject="English" students="29" teacher="Sulaiman"/>
<row subject="French" students="20" teacher="Simone"/>
<row subject="Spanish" students="23" teacher="Fernando"/>
<row subject="German" students="11" teacher="Bosch"/>
</inline>
</data>
...
</provider>
<contents>
...
<demo:table name="rosterTable"
tableData="${uix.data.Roster.row}"
tableState="${uix.data.tableStates[0]}">
<contents>
<demo:sortableColumn key="subject" header="Subject"/>
<demo:sortableColumn key="teacher" header="Teacher"/>
<demo:sortableColumn key="students" header="Students"/>
</contents>
...
</demo:table>
...
</contents>
</dataScope>
最後に、モックアップ表のイベント・ハンドラを登録する必要があります。
<handlers>
<!-- the examples in this file require an HttpSession to be created
for each user -->
<event name="null">
<method class="oracle.cabo.doc.demo.table.MockupUtils"
method="createSession"/>
</event>
<!-- handle all the table events -->
<event name="goto sort show hide">
<method class="oracle.cabo.doc.demo.table.MockupUtils"
method="handleTableEvent"/>
</event>
</handlers>
これで、Book表の例は終了です。 環境でHttpSession
を使用している場合、この例で使用されているクラスおよびコードを(若干変更して)簡単にアプリケーションに適合できます。
Copyright © 2001, 2004, Oracle Corporation.
All rights reserved.