ナビゲーション・ヘッダーをスキップ
Oracle ADF UIX開発者ガイド 目次へ
目次
前のページへ戻る
前へ
次のページへ進む
次へ

9. ADF UIXでの表の作成

tableは、Oracle ADF UIX Componentsテクノロジで使用可能な最も強力で複雑なユーザー・インタフェース・コンポーネントです。 表形式のデータを表示または更新するWebアプリケーションでは、tableを1つ以上使用する必要があります。 このトピックでは、tableコンポーネントの構成方法をすべて調べます。

ここでは、次の項目について説明します。

Tableの必要性

Tableで使用可能なオプションを見ると、どんな問題でも解決できるように考えられますが、そうではありません。 Tableは、2次元の形式で記述されたデータを表示(場合によっては更新)するために用意されています。 Tableを使用すると効率的な場合をまとめると、次のようになります。

これらに当てはまらない場合、Tableは使用に適した部品ではありません。 次の経験則は、Tableの使用が適切な場合を示していますが、すべての状況に適用されるわけではありません。

一方、Tableを使用できない状況もあります。

前述の条件を踏まえても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>

デモのコードにいくつかの項目を追加しました。

  1. 表を<dataScope>要素に配置し、表にデータソースを指定しました。 表自体は<dataScope>のコンテンツ部に移動され、データは<provider>セクションに配置されています。
  2. データはUIX XMLのインライン・データ構文を使用して指定し、この例ではdemoTableDataという名前で格納します。 demoTableDataの内部では、3つのdemoRowData要素を宣言しました。 この3つのデータ要素の名前はすべて同じであるため、UIX XMLはこれらを1つのDataObjectListにマージします。マージされたDataObjectListは要素3つ分のサイズで、各要素はDataObjectListの索引付けされた3つのDataObjectの1つを表します。
  3. table要素には、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>

これで、行ごとにコンテンツの異なる表ができました。 変更箇所は次のとおりです。

  1. demoRowDataという4つのDataObjectそれぞれにデータを1つ追加しました。このデータは、DataObjectがキーsomeTextで問合せされた場合に返されるテキスト文字列です。 結果は行ごとに変えました。
  2. 最初の列スタンプにあるテキストの値をバインドし、これがキーsomeTextで現行のDataObjectを問い合せた結果として返されるようにしました。 属性値として"${uix.data.aNamedDataObject.someText}"ではなく"${uix.current.someText}"を使用したため、この問合せは、特定のDataObjectではなく、現行指定されたDataObjectに対して行われます。 したがって、各行にスタンプするテキストは、表データから取得されます。
  3. 最後に、同じ手法を使用し、第2列のボタン・スタンプのテキストを変更します。 ただし、ボタンのリンク先はすべての行で同じままにしてあるので注意してください。属性をすべてデータ・バインドする必要はありません。

明確にするため、テキスト・スタンプとボタン・スタンプで別のデータを使用するよう例を微調整します。

<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パーサーは、tableDataDataObjectListタイプであることを認識し、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での表データの作成

データ・バインドにより、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を作成します。 ArrayDataSetDataObjectおよび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つの作業を行いました。

  1. 新規のDataObjectListを作成しました。このDataObjectListのサイズは行数と同じで、各DataObjectには、headerTextキーで格納された、行ヘッダーに表示するテキストが含まれています。
  2. 表のrowHeaderDataがデータ・バインド属性から取得されるよう登録しました。この属性の値は、demoTableDataという名前のDataObjectの下にあるdemoRowHeaderDataというDataObjectListです。 これにより、表で新規のDataObjectListを使用して、行ヘッダー・スタンプにデータを提供できます。
  3. 最後に、テキストの行ヘッダー・スタンプの値を、現行の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属性をデータ・バインディングすることにより名前変換をプライベートに処理することのみです。 自動的な名前変換は、tablenameTransformed属性を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");

最後のステップは、すべてを正しく動作させるために必要です。 tableproxied属性を、trueに設定する必要があります(この属性については後のセクションで説明します)。 これで、通常の方法でPageEventFlattenedDataSetを使用して、表の入力要素の値を取得できるようになります。 例にあげたようなラジオ・グループ列の値を取得するには、次のように指定します。

  DataSet tableInputs = new PageEventFlattenedDataSet(event,
                                                      "myTestTable");
  Object radioValue = tableInputs.selectValue(null, "radio");

レコード・ナビゲーション

いくつかの行がある表の作成方法を見てきましたが、アプリケーションのデータ・セットに多くの項目が含まれることがよくあります。 大企業の従業員をリスト表示する表などでは、すべてのレコードを同時に表示できないことは明白です。 このため、レコード・ナビゲーションを使用して、このような表を管理しやすいように分割する必要があります。

わずかなデータを追加することにより、表のBeanで、現行のデータ行がより大きなデータの一部であることを示すナビゲーション領域をレンダリングできます。 表の各行には索引番号が割り当てられ、どの行番号が現在表示され、どの行番号が表示されていないかがユーザーに通知されます。 このナビゲーション領域をレンダリングするには、表に追加属性をいくつか指定する必要があります。

不明な場合は、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)および現在の先頭の索引で作成されます。 最後に、このDataObjectListDataObjectに配置します。 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

前述した例では、すべてのナビゲーション・リンクが現在のページをポイントしています。 一部のアプリケーションでは、ナビゲーション・リクエストを他の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つを降(昇)順にソート可能にすることで、この機能をデモンストレーションしてみます。 新規のSortableHeaderBeansortable属性をこれらの値にデータ・バインディングすると、列ヘッダー・スタンプの外観がどのように変化するかに注意してください。

<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 列ヘッダー・セルのSortableHeaderBeanvalue属性として提供される任意のものを示します。 デフォルト値は、列のゼロから始まる索引です。
state STATE_PARAM 列ヘッダー(valueパラメータで指定)が、すでにソートされているかどうかを示します。 すでにソートされている場合、このパラメータは、列のソート方法に応じてUIConstant SORTABLE_ASCENDINGまたはUIConstant SORTABLE_DESCENDINGになります。 列がまだソートされていない場合、state値としては何も送信されません。

次のセクションでは、サーバー上でソートを処理する方法の例を示します。 この例は、一般的な環境で動作するよう設計されているため、本来必要である以上に複雑になっています。

最初の手順は、SortableHeaderBeanvalue属性の指定です。 各列ヘッダーの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を使用するために、SortableHeaderBeansortable属性をデータ・バインドします。 最初に、この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>

最初は、どの行も選択済としてマークされていないことに注意してください。 初期選択項目は、選択が必要な行の(ゼロから始まる)索引を、singleSelectionselectedIndex属性で指定することにより設定できます。

<singleSelection selectedIndex="1" />

次の手順は、選択項目に対してどんな処理が可能であるかをユーザーに知らせることです。 これには、singleSelectiontext属性を使用します。 ユーザーが処理を開始する際にボタンをクリックできるように、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と同様に、multipleSelectiontext属性および索引付けされた子をサポートします。 次に例を示します。

<table ... >
  <tableSelection>
   <multipleSelection text="Select record and ...">
    <contents>
     <submitButton text="Copy" event="copy"/>
     <submitButton text="Delete" event="delete" />
    </contents>
   </multipleSelection>
  </tableSelection>

  ...
</table>

multipleSelectionselected属性および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のディテール公開機能により、この機能が提供されます。

ディテール公開は、tabledetailという名前の付けられた子を設定することによりオンになります。 この子は、ユーザーが行の詳細を要求したときのみレンダリングされる点を除き、他のスタンプと同様です。 したがって、この子は現在の行の詳細をすべて表示できます。 次に例を示します。

<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としてレンダリングされる各矢印の公開状態は、tabledetailDisclosure属性により制御されます。 これは、表の各行に対応する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 表の名前

次に、tablefooterという名前の付けられた子であるtableFooter Bean内でtotalRowを使用した例を示します。また、この例では、右端のcolumnfooterという名前の付けられた子で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番目の列では、columnDataFormatnumberFormatに設定されています。 仕様では、数値は右揃えにするよう規定されているため、この表の列のデータ・セルはそれに従って配置されます。 最後に、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
説明
columnDataFormat
COLUMN_DATA_FORMAT_KEY
表自体にはコンテンツのタイプを認識する機能がないため、この属性を使用して、表の列に表示されるコンテンツのタイプを指定します。 異なるデータ書式を指定すると、データ・セルの位置揃えが変更されます。
  • textFormat: ほとんどの列スタンプに使用され、テキスト・データを含めるために使用されるデフォルトの書式。 現在は、左揃えになります。
  • numberFormat: この書式を指定するとデータが右揃えになり、数値が読みやすくなるため、データ・セルに数値が含まれている列に対して指定します。
  • iconButtonFormat: アイコンおよびボタンは、この書式の値により中央揃えにすることができます。
cellNoWrapFormat
CELL_NO_WRAP_FORMAT_KEY
セルのコンテンツは、複数の行にまたがって表示しないほうがよい場合もあります。 折返しをしないほうが適切なデータ列については、この属性をtrue(Boolean.TRUE)にします。 折返しを禁止すると表の幅が広くなりすぎることがあるため、これはデフォルトの動作ではありません。
width
WIDTH_KEY
表の余分な幅のうち、この列が取得する幅のパーセントを指定するために使用されます。 表にコンテンツで必要とされる以上の領域がある場合は、その領域を列に配分します。 ただし、一部の列(ボタンなど)には、与えられた最小の領域よりも多くの領域は必要ありません。 テキスト列などのその他の列で、コンテンツの表示に必要な行を削減できる場合は、追加の領域を使用するほうが理想的です。 width属性のとる値は、50%や100%などのパーセント値で、列が取得する追加領域の割合を示します。 すべての列の合計は100%になる必要があります。
displayGrid
DISPLAY_GRID_KEY
デフォルトでは、グリッド線はすべての列の左に表示されます。 場合によっては、垂直のグリッド線の数を減らし、他の列間の関係と比較して一部の列間の関係を強調するようにする場合もあります。 特定のデータ列の前、つまり左側の垂直グリッド線をオフにする場合は、この属性をfalseに設定します。
bandingShade
BANDING_SHADE_KEY
列のバンドの濃淡。 詳細は、後述の「バンド」のセクションを参照してください。
  • dark: デフォルトの書式。 列が暗い色で表示されます。
  • light: 列が明るい色で表示されます。

これらすべての列書式の値は厳格なルールではなく、ヒントにすぎません。 一部のレンダリングの外観やプラットフォームでは、すべての書式の値がサポートされているとはかぎりません。そのため、あるエージェントで特定のルールが通用しない場合もあります。 書式は依存性に欠けるため、アプリケーションにとって重要ではありません。

行書式

垂直グリッド線の表示場所を列で制御できることと同様に、行の水平グリッド線の表示場所をカスタマイズすることもできます。 このカスタマイズは、次の例で示すように、表の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>

ただし、列バンドは任意のパターンでも実行できます。 これにより、個々の列を、通常の変更パターンを使用せずに明るい色にすることができます。 このためには、列の複雑属性columnFormatbandingShade属性を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;
}

選択の処理

表が単一選択モードで使用されている場合は、TableProxygetSelectedRow()メソッドを使用し、現在選択されている行の索引を取得できます。 次に例を示します。

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>要素のリファレンスを参照してください。