OpenLaszloでマルチデバイス対応RIAを作ろう

第9回データの取り扱い[その2]

前回はXMLデータを取得して表示する基本を解説しました。今回は、データの細かい操作をするための方法について解説します。データによって処理をかえたり、データを加工して表示したり、ビュー間でデータパスの受け渡しをしたりといったことができるようになります。

データポインタ

データポインタとは

前回出てきたデータパス(datapath)は、主にビュー(文字列など画面上の部品のことです)にdatapath属性として設定し、そこで指定したXPath条件に合うデータをすべて表示するために使います。

それに対してデータポインタは、XMLデータの階層構造の中から1つの階層のみを指定するために使います。データポインタは表示するためというより、1つ1つデータを取り出しては何か処理するという用途に向いています。

データポインタの基本形

実例で説明します。リスト1を実行すると画面上に「世界」とだけ表示されます。処理内容を一言で言うと、<dataset>の直下にある<world>のname属性の値だけを選んで表示、です。

リスト1
<canvas proxied="false" bgcolor="0xffffcc"> 
  <dataset name="ds" >
    <world name="世界">
      <country area="ヨーロッパ" name="イタリア" />
      <country area="ヨーロッパ" name="ギリシャ" />
      <country area="アフリカ" name="ケニア" />
      <country name="日本" />
      <space name="月" />
    </world>
  </dataset>
  <simplelayout/>
  <text id="txt"/>
  <datapointer xpath="ds:/"><!-- ① -->
    <handler name="ondata">
      this.selectChild(); // ②
      var t = this.xpathQuery('@name'); // ③
      txt.setAttribute('text',t); //④
    </handler>
  </datapointer>
</canvas>

以下、リスト1の処理を説明します。

① <datapointer>でデータポインタを宣言します。そのxpath属性で、どの<dataset>のどの階層位置に最初のポインタを置くかを指定します。ポインタというのは、XMLデータの階層構造の中でどの位置を指すか、という指定行為のことです。リスト1ではxpath="ds:/"なので、dsという名前のデータセットのルートつまり<dataset name="ds">の位置にポインタを置いたことになります。

② thisは自分自身つまりデータポインタを指します。selectChild()というメソッドはデータポインタのメソッドで、XMLデータ構造の子の方向へ(=下の階層へ)ポインタを移動するという意味です。ここでポインタは<dataset name="ds">から1階層下の<world name="世界">に移動します。

③ xpathQuery()は、XPathの条件に合うデータを返すメソッドです。使い方はビューのdatapath属性を使うときと同じ感じなのですが、<datapath>は(条件に合致する)値が複数返ってくるのに対して、xpathQueryは値を1つだけ返すところが異なる点です。ここでは、②の処理によって<world name="世界">にすでにポインタがあり、その位置のままで「@name」を指定しているので、⁠世界」が取り出されて変数tに格納されています。

④ txtのtext属性値、つまり<text id="txt"/>用の文字列データとして、変数tの内容(=「世界⁠⁠)を代入しています。つまり、変数tのなかの「世界」という文字列が<text>によって画面に表示されたという仕組みです。

データポインタの操作では、selectChild()、xpathQuery()は必ずと言っていいほど良く使います。ちなみに親方向の階層(上の階層)に移動するにはselectParent()を使います。

データポインタのループ処理

データポインタはXMLデータ中の1つの階層にのみアクセス可能なので、複数データを処理するにはループ処理をする必要があります。よく使うのはdo-whileです。do-whileでは、whileの条件式がtrueの間、doブロック内の処理を繰り返します。条件判断式がdoブロックの後にあるので、最低1回は処理されます。リスト2は全データの中からareaがヨーロッパとアフリカの国名だけを表示するコードです。実行結果は「イタリア、ギリシャ、ケニア」となります。

リスト2
<canvas proxied="false" bgcolor="0xffffcc"> 
  <dataset name="ds" >
    <world name="世界">
      <country area="ヨーロッパ" name="イタリア" />
      <country area="ヨーロッパ" name="ギリシャ" />
      <country area="アフリカ" name="ケニア" />
      <country name="日本" />
      <space name="月" />
    </world>
  </dataset>
  <simplelayout/>
  <text id="txt" multiline="ture"/>
  <datapointer xpath="ds:/"><!-- ① -->
    <handler name="ondata">
      this.selectChild(2); // ②
      do{
        var area = this.xpathQuery('@area'); // ③
        if(area=="ヨーロッパ" || area=="アフリカ"){   // ④
          var name = this.xpathQuery('@name'); // ⑤
          txt.addText(name + "\n"); //⑥
        }
      }while(this.selectNext());  //⑦
    </handler>
  </datapointer>
</canvas>

① データポインタを宣言し、ds:/にポインタを置いています。

② selectChild(2)は2階層下に移動するという意味です。selectChild(2)の括弧内の数字は移動する階層の数で、省略すると1になります。この例では、<dataset name="ds">から2階層下の1番目のデータ<country area="ヨーロッパ" name="イタリア" />にポインタが移ります。

③ area属性値を変数areaに格納しています。

④ area属性値がヨーロッパあるいはアフリカかどうかを判断しています。

⑤ name属性値を変数nameに格納しています。

⑥ addText()は文字列を追加するメソッドです。nameに改行コード(\n)を追加して、txt(<text>)に文字列として追記しています。<text>にmultiline="true"を設定することで複数行の表示に対応させています。

⑦ selectNext()は、同一階層の次のデータにポインタを移動するメソッドです。この例では1番目の<country area="ヨーロッパ" name="イタリア" />に対する処理が終わったら、次の<country area="ヨーロッパ" name="ギリシャ" />にポインタが変わることになります。selectNext()がfalseを返したとき、つまり同一階層内でポインタを移動するデータがなくなったときにこのループは終了します。

ということで、リスト2は<world>直下の全データ5つを1つずつ処理・加工していくという動作になります。このようにデータポインタを使えば、データに対する細かい処理が可能になります。

データポインタのコピー

データポインタをデータパスとして、他のビューのデータパスにコピーすることができます。具体的に何ができるかというと、XMLデータから取得した名前のみの一覧リストを表示して、どれか1つを選んだらその詳細データを表示する、というようなことができます。

リスト3は、動作サンプルでは国名だけを一覧表示していますが、国名をクリックすると、その国の他のデータも表示するサンプルです。

リスト3
<canvas proxied="false" bgcolor="0xffffcc">
  <dataset name="ds" >
    <data country="イギリス" capital="ロンドン" lang="英語" /> 
    <data country="イタリア" capital="ローマ" lang="イタリア語" /> 
    <data country="オランダ" capital="アムステルダム" lang="オランダ語" /> 
    <data country="スペイン" capital="マドリード" lang="スペイン語" /> 
    <data country="フランス" capital="パリ" lang="フランス語" /> 
    <data country="モナコ公国" capital="モナコ" lang="フランス語" /> 
    <data country="ルーマニア" capital="ブカレスト" lang="ルーマニア語" /> 
  </dataset>
 
  <vbox id="details" x="80" width="100" bgcolor="white"><!-- ① -->
    <datapath/><!--②-->
    <text datapath="@country" fgcolor="red" fontstyle="bold"/>
    <text datapath="@capital"/>
    <text datapath="@lang"/>
  </vbox>
 
  <vbox>
    <text datapath="ds:/data/@country"> <!--③-->
      <handler name="onclick">
         details.datapath.setFromPointer(this.datapath); //④
      </handler>
    </text>
  </vbox>
</canvas>

リスト3サンプル
国名をクリックすると詳細表示が出ます

① 配下に3つの<text>がある<vbox>ですが、空の<datapath>が設定されています。つまり<text>および<vbox>に実データがないので、このビューは最初は画面に表示されていません。

② 親の<vbox>に作用する<datapath>です。実は<vbox datapath="" >と書いているのと同じことです。見た目をわかりやすくするために別タグにしています。①ですでに書いたのと同じことですが、datapathの値が空なのでレプリケーションが発生せず、<vbox>は画面に表示されていません。

③ ds:/data/@countryに合致するデータがすべてレプリケーションによって複製表示されます。親が<vbox>なので、実際に画面上に国名リストとして表示されている部分になります。

④ onclickハンドラの中に書かれているので、国名をクリックしたら実行されるスクリプトです。details.datapath.setFromPointer(this.datapath)というのは、detailsビューのデータパス<datapath>のポインタを、クリックしたときの国名の元になっているデータパス(this.datapath=ds:/data/@country)にセットする、という意味です。つまり、オランダがクリックされたら「ds:/data/@countryの3番目」がdetailsのdatapathにセットされることになり、details配下の<text>3つのdatapathはそれぞれds:/data[3]/@country、ds:/data[3]/@capital、ds:/data[3]/@langと指定したのと同じになります。

おすすめ記事

記事・ニュース一覧