japan.internet.com
japan.internet.com メンバーID
Twitter
Facebook
RSS
ピックアップ
2009年12月18日 10:00
EXTJS でつくる RIA の世界
EXTJS でつくる RIA の世界 株式会社環(かぶしきがいしゃ かん)メールホームrss
各種ソフトウェアやWebサービスと連携し、アクセス解析機能を容易に実装できるアクセス解析サービス"Web Mining Service"を中心に、アクセス解析ソリューションをご提案します。

EXTJS のグリッドパネルを使ってみる

■はじめに
前回のコラムでは、2回に分けてEXTJSのツリーパネルクラスを紹介させて頂きました。
今回は、グリッドパネルクラスをご紹介します。こちらも導入編と応用編の2回に分けてご紹介させて頂きます。
グリッドパネルクラスも、EXTJSに最初から用意されているウィジェットクラスです。前回までで、ご紹介したツリーパネルクラスと同様に、こちらもパネルクラスを継承して作られています。グリッドパネルクラスは、関連する複数のクラスを使う関係上、扱いが他のクラスと比較して多少複雑ですが、その分、柔軟な構成や表示が行えるよう工夫されています。

■グリッドパネルクラス
グリッドパネルクラスでは、Excelような行列の表示が可能となります。ですので、何らかのデータを一覧表示するようなケースに用いられるものとなります。またページングツールバーを並行して用いるとある一定以上の行(つまり表示件数)がある場合、ページングを行うような処理にすることもできるようになっています。
EXTJSでは、グリッドパネルクラスを、
Ext.grid.GridPanel
として定義していますので、これをインスタンス化することにより以下のような画面を表示することができます。

グリッドパネルクラス1
グリッドパネルクラス1
*クリックして拡大
今回も、Ajaxを使ったサンプルもご紹介したいと思います。サーバ側コードはPHP5.2.xで記載しておりますので、サンプルの動作をご確認される際には、同等程度の環境が必要となる点、ご了承ください。

■簡単な例
まず始めに用語や関連するクラスから、簡単に説明させていただきます。

グリッドパネルを使用するために、ストア(Ext.data.Store)というクラスを使用することになります。イメージ的にはExcelのシートのようなものだと思ってください。グリッドパネルクラスは、このストアに格納されているデータを表示・操作するように構成されています。
ストアクラス内では、レコード(Ext.data.Record)というクラスを格納します。こちらは、グリッド上に表示したい1行のデータを表します。簡潔に表現すると、レコードクラスの集合体がストアクラスであり、そのストアクラス内のデータを表示してくれるものが、グリッドクラスだということになります。
このため、グリッドへ表示するデータの取得や加工などは、このストアクラスを通して行うことになります。

グリッドパネルクラス2
グリッドパネルクラス2
*クリックして拡大
実際には、もう少し複雑な構成な構成で、かなり柔軟な処理が行えるようになっていますが、そのあたりは必要に応じて調べてみてください。

では早速ですが、さきほどのキャプチャイメージを実現させるためのサンプルコードを以下に示します。

――――――――――――――――――――――――――――――――
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>グリッドパネルクラスサンプル</title>
 
  <link rel="stylesheet" type="text/css" href="extjs/resources/css/ext-all.css">
 
  <script type="text/javascript" src="extjs/adapter/ext/ext-base.js"></script>
  <script type="text/javascript" src="extjs/ext-all-debug.js"></script>
 
  <script type="text/javascript">
    Ext.BLANK_IMAGE_URL = 'extjs/resources/images/default/s.gif';
   
    Ext.onReady( function()
    {
  var testData = [
   ['user01', '市山', '肇',     32, '1977-01-01'],
   ['user02', '双葉', '次郎',   36, '1973-02-02'],
   ['user03', '狭山', '光江',   49, '1960-03-03'],
   ['user04', '司馬', '静子',   23, '1986-04-04'],
   ['user05', '五味', '吾郎',   25, '1984-05-05'],
   ['user06', '六場', '睦',     38, '1971-06-06'],
   ['user07', '名鳥', '菜波',   23, '1986-07-07'],
   ['user08', '八重', '靖男',   47, '1962-08-08'],
   ['user09', '久喜', '熊次郎', 49, '1960-09-09'],
   ['user10', '戸塚', '俊夫',   18, '1991-10-10'],
          
   ['user11', '一場', '泉',     32, '1977-01-11'],
   ['user12', '藤山', '房子',   36, '1973-02-12'],
   ['user13', '三葉', '早苗',   49, '1960-03-13'],
   ['user14', '滋賀', '芳之',   23, '1986-04-14'],
   ['user15', '後藤', '逸美',   25, '1984-05-15'],
   ['user16', '武藤', '六三郎', 38, '1971-06-16'],
   ['user17', '名波', '直秀',   23, '1986-07-17'],
   ['user18', '矢島', '弥生',   47, '1962-08-18'],
   ['user19', '熊田', '久太郎', 49, '1960-09-19'],
   ['user20', '豊橋', '利美',   18, '1991-10-20']
  ];
  
  
  var gridPanel = new Ext.grid.GridPanel( {
   title:            'グリッドパネルクラス-サンプル',
   width:            600,
   height:           300,
   stripeRows:       true,
   autoExpandColumn: 'birthday',
   store:    {
    xtype  : 'arraystore',
    fields : [
     {name: 'userId',    type: 'string'},
     {name: 'userName1', type: 'string'},
     {name: 'userName2', type: 'string'},
     {name: 'age',       type: 'int'   },
     {name: 'birthday',  type: 'date',  dateFormat: 'Y-m-d'}
    ],
    data: testData
   },
   columns:  [
    {id: 'userId',    header: 'ユーザID',     width: 70,  sortable: true, dataIndex: 'userId'},
    {id: 'userName1', header: 'ユーザ名(姓)', width: 160, sortable: true, dataIndex: 'userName1'},
    {id: 'userName2', header: 'ユーザ名(名)', width: 160, sortable: true, dataIndex: 'userName2'},
    {id: 'age',       header: '年齢',         width: 50,  sortable: true, dataIndex: 'age'},
    {id: 'birthday',  header: '誕生日',       width: 100, sortable: true, dataIndex: 'birthday', renderer: Ext.util.Format.dateRenderer('Y年m月d日')}
   ],
   applyTo:  'p'
  } );
 } );
  </script>
</head>


<body>

<br><br><br>
<div id="p" style="width: 600px; margin: 0 20em;">
</div>


</body>
</html>
――――――――――――――――――――――――――――――――


このサンプルでは表示データを配列の形で持たせています。
また、その配列データを、ストアを構築しそのストアに設定しているコードが、以下の部分となります。

store:    {
xtype : 'arraystore',
fields : [
{name: 'userId',    type: 'string'},
{name: 'userName1', type: 'string'},
{name: 'userName2', type: 'string'},
{name: 'age',       type: 'int'   },
{name: 'birthday', type: 'date', dateFormat: 'Y-m-d'}
],
data: testData
},

今回は、Ext.data.Storeクラスを継承して予め用意されている、Ext.data.ArrayStoreクラスを使用しています。
このクラスは、今回のような配列値を容易に扱うためのストアクラスとなっています。
使用するストアクラスの指定は、「store」パラメータに対して、使用するストアクラスのインスタンスを設定しますが、今回のサンプルコードでは、「xtype」パラメータによる指定にしています。これは、
  new Ext.data.ArrayStore({ ・・・ })
と同義になっています。

ストアクラスの「fields」パラメータには、データにおける各列の
・名称定義
・型の指定
を行っています。これは、取り込まれるデータ(今回は配列にて設定されるデータ)の各列がどのような型であり、また後々の内部利用がしやすいように各列に名前付けを行っています。これを1カラムとして、必要カラム分を配列として、「fields」パラメータに設定します。
データ型が「date」の場合には、「dateFormat」パラメータを指定します。これはそのデータがどのような形式にて、日付が表されているか指定するものとなっています。

取り込みたいデータの指定は「data」パラメータにて行っています。ここに予め用意しておいたデータを設定します。

さて、グリッドパネルの各パラメータへ戻ります。
表示するカラム(列)の指定は「columns」パラメータにて行います。こちらも1カラムを1要素として、表示したいカラムを配列値として設定します。

columns: [
{id: 'userId',    header: 'ユーザID',     width: 70, sortable: true, dataIndex: 'userId'},
{id: 'userName1', header: 'ユーザ名(姓)', width: 160, sortable: true, dataIndex: 'userName1'},
{id: 'userName2', header: 'ユーザ名(名)', width: 160, sortable: true, dataIndex: 'userName2'},
{id: 'age',       header: '年齢',         width: 50, sortable: true, dataIndex: 'age'},
{id: 'birthday', header: '誕生日',       width: 100, sortable: true, dataIndex: 'birthday',
renderer: Ext.util.Format.dateRenderer('Y年m月d日')}
],


各要素ですが、
  id :その列のid値。ユニークなidを設定する必要がある。
  header :表示される列タイトル。
  width:カラム幅。
  sortable :ユーザ側操作でソート可能列とするかどうかの指定。
  dataIndex :この列に表示させたい、ストア中の列名。
これは先ほどのストアオブジェクトで指定したnameパラメータ値と表示列を
バインディングするためのものです。
という指定になっています。

「誕生日」列だけ「renderer」パラメータが存在しますが、これは、ストア中のデータを加工してその列に表示する際にレンダラー処理を行うための関数を指定する項目となります。
今回は、誕生日列に日本語表記にした日付を表示させるために、EXTJSで予め用意されているレンダラー関数を使用しています。

その他に、「stripeRows」パラメータを使用していますが、こちらは各行の背景色を交互に変更するための指定であり、「autoExpandColumn」は、表示されるグリッドパネルの横幅に合わせて、余分な枠が存在する場合には、ここで指定されているカラムidの列幅を、(強制的に)広げて余分な領域が出現しないようにするための指定となります。

今までのウィジェットと比べ、かなり濃い内容かと思います。しかし、コード的には、それほど長くなく、その上でこれだけのRIAを実現できるというのは、かなりの生産性があるのではないでしょうか。

■Ajaxによる動的なデータ取得
今度は、ツリーパネルクラスのときにように、サーバ側に問い合わせを行い、データを取得するサンプルをご紹介します。動作は先ほどのサンプルと、ほぼ同じ動きとなります。
今回のサンプルは、HTMLコード、JavaScriptコード、サーバ側のPHPコードの3つがある点に注意してください。なお、JavaScriptコード中では、グリッドパネルクラスを継承してUserGridPanelクラスを生成しています。

―――――――――――――――HTMLコード―――――――――――――――
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>グリッドパネルクラスサンプル</title>
 
  <link rel="stylesheet" type="text/css" href="extjs/resources/css/ext-all.css">
 
  <script type="text/javascript" src="extjs/adapter/ext/ext-base.js"></script>
  <script type="text/javascript" src="extjs/ext-all-debug.js"></script>
 
  <script type="text/javascript" src="src_6_2.js"></script>
 
  <script type="text/javascript">
    Ext.BLANK_IMAGE_URL = 'extjs/resources/images/default/s.gif';
   
    Ext.onReady( function()
    {
  var gridPanel = new Ext.ux.UserGridPanel( {
   applyTo:  'p'
  } );
 } );
  </script>
</head>


<body>

<br><br><br>
<div id="p" style="width: 600px; margin: 0 20em;">
</div>


</body>
</html>
――――――――――――――――――――――――――――――――


―――――――――――――――JavaScriptコード―――――――――――――――
Ext.namespace( 'Ext.ux.UserGridPanel' );
Ext.ux.UserGridPanel = Ext.extend( Ext.grid.GridPanel,
{
 
 title:            'グリッドパネルクラス-サンプル',
 width:            600,
 height:           300,
 stripeRows:       true,
 autoExpandColumn: 'birthday',
 loadMask:         {
  msg:     '読み込み中。しばらくお待ちください...'
 },
 columns:          [
  {id: 'userId',    header: 'ユーザID',     width: 70,  sortable: true, dataIndex: 'userId'},
  {id: 'userName1', header: 'ユーザ名(姓)', width: 160, sortable: true, dataIndex: 'userName1'},
  {id: 'userName2', header: 'ユーザ名(名)', width: 160, sortable: true, dataIndex: 'userName2'},
  {id: 'age',       header: '年齢',         width: 50,  sortable: true, dataIndex: 'age'},
  {id: 'birthday',  header: '誕生日',       width: 100, sortable: true, dataIndex: 'birthday', renderer: Ext.util.Format.dateRenderer('Y年m月d日')}
 ],
 
 
 /* このクラス用に独自に用意したプロパティ */
 dataFields:       [
  {name: 'userId',    type: 'string'},
  {name: 'userName1', type: 'string'},
  {name: 'userName2', type: 'string'},
  {name: 'age',       type: 'int'   },
  {name: 'birthday',  type: 'date',  dateFormat: 'Y-m-d'}
 ],
 
 
 //*****************************************************************
 //initComponentのオーバーライド
 //*****************************************************************
 initComponent: function()
 {
  
  //
  //storeクラスを構築
  //
  this.store = this.createStore();
  
  
  //
  //親クラスのinitComponentを呼び出す
  //
  Ext.ux.UserGridPanel.superclass.initComponent.call( this );
 },
 
 
 //*****************************************************************
 //Storeを構築するための独自メソッド
 //*****************************************************************
 createStore: function()
 {
  //
  //Ajaxでのリストデータ読み込み用のHTTPプロキシを構築
  //
  var proxy = new Ext.data.HttpProxy( {
   url    : 'http://www.example.com/src_7_2.php',
   method : 'POST'
  } );
  
  
  //
  //配列形式のデータを解釈するためのリーダの構築
  //
  var reader = new Ext.data.ArrayReader(
   {
    idIndex: 0,
   },
   this.dataFields
  );
  
  
  //
  //データストアの構築
  //
  return new Ext.data.Store( {
   autoLoad:   true,
   remoteSort: true,
   proxy:      proxy,
   reader:     reader
  } );
 }
} );
――――――――――――――――――――――――――――――――

―――――――――――――――サーバ側PHPコード―――――――――――――――
<?php


$testData = array(
 array( 'user01', '市山', '肇',     32, '1977-01-01' ),
 array( 'user02', '双葉', '次郎',   36, '1973-02-02' ),
 array( 'user03', '狭山', '光江',   49, '1960-03-03' ),
 array( 'user04', '司馬', '静子',   23, '1986-04-04' ),
 array( 'user05', '五味', '吾郎',   25, '1984-05-05' ),
 array( 'user06', '六場', '睦',     38, '1971-06-06' ),
 array( 'user07', '名鳥', '菜波',   23, '1986-07-07' ),
 array( 'user08', '八重', '靖男',   47, '1962-08-08' ),
 array( 'user09', '久喜', '熊次郎', 49, '1960-09-09' ),
 array( 'user10', '戸塚', '俊夫',   18, '1991-10-10' ),
 
 array( 'user11', '一場', '泉',     32, '1977-01-11' ),
 array( 'user12', '藤山', '房子',   36, '1973-02-12' ),
 array( 'user13', '三葉', '早苗',   49, '1960-03-13' ),
 array( 'user14', '滋賀', '芳之',   23, '1986-04-14' ),
 array( 'user15', '後藤', '逸美',   25, '1984-05-15' ),
 array( 'user16', '武藤', '六三郎', 38, '1971-06-16' ),
 array( 'user17', '名波', '直秀',   23, '1986-07-17' ),
 array( 'user18', '矢島', '弥生',   47, '1962-08-18' ),
 array( 'user19', '熊田', '久太郎', 49, '1960-09-19' ),
 array( 'user20', '豊橋', '利美',   18, '1991-10-20' )
);



sleep( 2 );
echo json_encode( $testData );
?>
――――――――――――――――――――――――――――――――

HTMLコードに関しては、今までのコラムをご参照して頂いていれば、特に問題はないかと思います。
JavaScript側のコードですが、重要項目としてはStoreクラスの構築部分となります。

//*****************************************************************
//Storeを構築するための独自メソッド
//*****************************************************************
createStore: function()
{
//
//Ajaxでのリストデータ読み込み用のHTTPプロキシを構築
//
var proxy = new Ext.data.HttpProxy( {
url    : 'http://www.example.com/src_7_2.php',
        method : 'POST'
} );
   
   
//
//配列形式のデータを解釈するためのリーダの構築
//
var reader = new Ext.data.ArrayReader(
        {
idIndex: 0,
        },
        this.dataFields
);
   
   
//
//データストアの構築
//
return new Ext.data.Store( {
        autoLoad:   true,
        remoteSort: true,
        proxy:      proxy,
        reader:     reader
} );
}


Ajaxによるデータ取得の際には、構築するストアクラスの「proxy」パラメータにExt.data.DataProxyクラスのインスタンスを設定することにより実現します。今回は、Ajaxによるデータ取得ために、データプロキシクラスとして、Ext.data.HttpProxyを使用しています。名称通り、これはExt.data.DataProxyクラスを継承して用意されているクラスとなります。
データプロシキクラスは、ストアクラスでデータを取得(読み取る)際に、読み取り方法の動作を規定するクラスとなります。このクラス中の「url」パラメータで取得先URLの指定を行い、指定されたURLにアクセスして、必要データの取得を行っています。

また、Ext.data.Readerクラスのインスタンスを、構築するストアクラスの「reader」パラメータに設定することにより、Ajaxで得られたデータの解釈を規定できます。今回は、各レコードにおけるカラムデータを配列形式にて受け取るため、EXTJSで用意されているExt.data.ArrayReaderクラスを用いています。
このクラスのコンストラクタに対する第一引数には連想配列の形式にてパラメータ設定します。今回は「idIndex」パラメータのみ設定しています。このパラメータは、得られたデータ中のカラムの中で、どの位置のデータを、レコード区別のためのidとして使用するか設定するものとなります。
第二引数には、データにおける各列の
・名称定義
・型の指定
を行っています。これは先ほどの説明通りですが、設定内容に関しては、後々変更しやすいように、継承したクラスの独自プロパティとして持たせています。

最後にストアクラスのインスタンス化を行っています。ここで重要なのは「autoLoad」パラメータとなります。この値がtrueの場合、グリッドパネルが表示されると同時に、Ajax通信を開始しサーバへ問い合わせを行います。Falseの場合には、手動でロード(問い合わせ)を行う必要があります。

継承したUserGridPanelクラス中には、以下のような記述が存在します。

loadMask: {
msg:     '読み込み中。しばらくお待ちください...'
},


お気づきかと思いますが、読み取り中におけるウェイト表示の文言を設定しています。

グリッドパネルクラス3
グリッドパネルクラス3
*クリックして拡大
最後にサーバ側コードですが、こちらは、先ほどまでのサンプルでJavaScript中におけるサンプルデータをPHP化しただけのものとなります。それをjson_encode()関数によりJSON化して応答しています。
sleep()関数は、単に読み込み中の状態を確認して頂きたいために設定しているものですので、本来は必要ありません。

■応用編に向けて
今回のコラムでは、グリッドパネルクラスを使ってサーバ側から、配列で表されているデータを取得し、グリッド内に表示するところまでをご紹介させて頂きました。
次回は、より実践的なJSON形式によるAjaxを使った動的なデータ取得を行っていきます。また、イベント処理の追加や、セレクションの変更なども行っていきたいと思います。

記事提供:株式会社環

プリンター用
記事を転送
この記事をクリップ!
japan.internet.com Androidアプリ
japan.internet.com Androidアプリ Android で japan.internet.com のニュースがどこでも、いつでも読めて、ニュースをTwitterに直接つぶやいたり、Facebookにコメントできる。 人気ニュースランキング、ブックマーク機能なども使えます。詳しくは こちらから
注目のトピックス
Copyright 2012 internet.com K.K. (Japan) All Rights Reserved.