« 2008年05月 | メイン | 2008年12月 »



2008年06月 アーカイブ

2008年06月23日

Ext.grid.EditorGridPanelの使い方

editorgridsumple.PNG ExtJSのエントリはこちらにまとめてあります

ダウンロードページより、アーカイブ(Ext JS 2.1 SDK)をダウンロードして解凍してください。20080623時点のバージョンは ext-2.1 です。
今日は、ExtJSのEditor Grid Exampleを解説してみたいと思います。
ワタシが作ってみたEditorGridは、上記リンクのサンプルのようにプルダウンやチェックボックスは装備しておりません。EditorGridそのものは非常にシンプルに作った代わりに、編集されたセルを検出するボタンを追加してみました。Gridをフォームとしてどこかへポストしたいといった時に、有効かもしれません。あと、いきなりEditorGridPanelとか言われても・・と言う方にはここにExtJSのチュートリアルを簡単にまとめてみました。
まず、以下に今回解説するEditorGridPanelのソースを全て載せてみましたので、この実装方法とコンフィグを解説します。
動作サンプルとサンプルソース
Ext.onReady( function(){
    var id = 0;
    var reader = new Ext.data.ArrayReader( { id: 0 }, [
          { name: 'str', mapping: 1 },
       { name: 'num', mapping: 2 }
    ]);
    var store = new Ext.data.Store({
        reader: reader,
        data:[ 
            [ id++, 'hoge', 1 ],
            [ id++, 'moge', 2 ],
            [ id++, 'fuga', 3 ]
        ]
    });
    
    var clmnModel = new Ext.grid.ColumnModel([
        { header: "Title", width: 200, sortable: true, dataIndex: 'str', editor: new Ext.form.TextField() },
        { header: "Number", width: 100, sortable: true, dataIndex: 'num', editor: new Ext.form.NumberField() }
    ]);

    var grid = new Ext.grid.EditorGridPanel({
        store:store,
        colModel:clmnModel,
        renderTo:'renderTarget',
        title:'simple-editorgrid',
        stripeRows:true,
        height:200,
        width:320,
        frame:true,
        clicksToEdit:1,
        tbar:[
        { text: 'touch', handler:function(){
            grid.getStore().each( function( targetObj ){
                var touchField = targetObj.getChanges();
                for( name in touchField ){
                    alert(
                        "index : [ " + targetObj.id + " ]\n" + 
                        "fieldName : [ " + name + " ]\n" +
                        "value : [ " + touchField[ name ] +" ]\n" 
                    );
                }
                targetObj.commit();
            });
        }}]
    });
});

【Ext.grid.EditorGridPanelの解説】

それではEditorGridPanelを見ていきたいと思います。普通のGridPanelの使い方はこちらを参照ください。

EditorGridPanel

    var grid = new Ext.grid.EditorGridPanel({
という箇所がエディターグリッドパネルそのものになります。GridPanelと違うところはコンフィグオプションの
     clicksToEdit:1,
という個所と、カラムモデルにeditorというコンフィグオプションを適用しているところだけです。
clicksToEditのAPIDocをみると
The number of clicks on a cell required to display the cell's editor (defaults to 2)
とありまして、セルが編集可能になるクリック数みたいなことが書いてありますが、なんかあんまり関係ないような気がします。(値がいくつでもダブルクリックで編集可能になった)このコンフィグオプションがなくても編集可能になりました。
で、実際に編集可能にするためにはカラムモデルにeditorという設定をしなければならないようです。
    var clmnModel = new Ext.grid.ColumnModel([{ 
        header: "title", 
        width: 200, 
        dataIndex: 'str', 
        editor: new Ext.form.TextField()  // <-コレ
    },
ここのeditorにformパッケージのクラスでnewしてあげると、編集時にそのクラスで動くようになります。例えばComboBoxでnewしてあげるとプルダウンできるようになります。(適宜ComboBoxのコンフィグは必要になります)
これでEditorGridPanelが完成しました。

編集セルの特定

冒頭で記述しましたようにEditorGridの編集個所が特定できないと、どこかのページに値をポストしようにも出来ませんので、この解説をしたいと思います。ポストの前にとりあえず、gridのデータをどこかに渡すためには、
  1. どのレコードの、
  2. どのフィールドで
  3. どんな値に
変更されたのか?という情報は必要になってきます。今回はその情報を取り出すだけで、データをポストするところまで組んでいません。
tbar:[
{ text: 'touch', handler:function(){
    grid.getStore().each( function( targetObj ){
        var touchField = targetObj.getChanges();
        for( name in touchField ){
            alert(
                "index : [ " + targetObj.id + " ]\n" + 
                "fieldName : [ " + name + " ]\n" +
                "value : [ " + touchField[ name ] +" ]\n" 
            );
        }
        targetObj.commit();
    });
}}]
というわけで、実装の解説を進めていきますが、tbarというコンフィグオプションは前回のTreePanelのときに解説しましたツールバーです。今回はツールバーにtouchというボタンを与えてあげて、そこにハンドラとして編集セルを見るメソッドを用意しました。
まず、gridが用意しているgetStore()メソッドを呼び出して、データストアを取得します。次に返ってきたstoreで持っているeachメソッドを使って、レコード単位に全てのオブジェクトを拾いあげます。
grid.getStore().each( function( targetObj ){
パラメータのtargetObjはExt.data.Recordのオブジェクトが入ってきますので、このオブジェクトに変更があったかどうかを聞きます。
var touchField = targetObj.getChanges();
getChanges()メソッドはAPIDocに以下のような説明がありました。
Gets a hash of only the fields that have been modified since this Record was created or commited.
「レコードが作られたか、あるいは、コミットされた後に編集されたフィールドをハッシュ(オブジェクト)で返すよー」と言っているみたいです。なるほど。便利。編集されてなければ返されたオブジェクトはプロパティを持たないようです。ですので、for~inで調べてみました。これで、編集されたフィールドと値を取得することが出来ます。
ちなみに、targetObjの実態はExt.data.Recordですので、idを持っていて自分がどのレコードかを知っているのですが、今回の実装はストアを作るときに明示的にidを振っています。
    var id = 0;
    var reader = new Ext.data.ArrayReader( { id: 0 }, [
          { name: 'str', mapping: 1 },
       { name: 'num', mapping: 2 }
    ]);
    var store = new Ext.data.Store({
        reader: reader,
        data:[ 
            [ id++, 'hoge', 1 ],
            [ id++, 'moge', 2 ],
            [ id++, 'fuga', 3 ]
        ]
    });
前回作ったGridはSimpleStoreを使って簡単にデータストアを構築しましたが、今回はレコードのidも見たかったのでちょっと構築方法が違います。
ArrayReaderのコンストラクタの第1パラメータで、「レコードのidは扱うデータ(配列)の0番目ですよ」と明示的に指定しています。さらに第2引数でレコードのnameと扱うデータ(配列)の何番目かをmappingで指定しています。マッピングをずらすと、grid内にidを表示することも出来ます。idの生成やその一意性は実装依存ですが、自ら生成しなくとも勝手に割り当てられるようです。ただ、一意に割り当ててあげないとバインド出来ませんね。
これでソートしてレコードの位置が変わってもidでバインドできるようになりました。ちなみにレコードが上から何番目か?ってのを知りたい場合は、
grid.getStore().indexOf( targetObj )
というstoreのindexOfでそのレコードが上から何番目に位置するのか聞けます。
で、最後にtargetObj.commit();で編集をコミットしてあげています。

というわけで、EditorGridPanelの解説をしてみました。ワタシの理解を多分に含めておりますので、誤った情報である可能性がありますのでご注意ください。

[ スポンサードリンク ]

2008年06月26日

Ext.DomQueryの基本的な使い方#1

domQuery.PNG ExtJSのDOMセレクタはExt.query()というメソッドで使えるようになっています。Ext.query()メソッドは2つのパラメータを持ち、第1パラメータに取得したい場所を書式に基づいた文字列で与えて、第2パラメータにエレメントのidを渡します。第2パラメータは省略可能ですのでこれを使わずとも、第1パラメータだけでかなり柔軟な取得ができます。また、Ext.query()メソッドはExt.DomQuery.select()のエイリアス(shorthand)ですので、どちらを使ってもいいようです。
動作サンプル

DomQueryで探索するサンプルHTML

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
  <div id="fuga" class="piyo">
   ここはdiv#fuga.piyoです。
   <span class="fuga">
    ここはspan.fugaです。div#fuga.piyoの中に配置されています。
   </span>
   <a href="#" class="piyo">
    ここはa.piyoです。span.fugaと同じ高さで、その直後に配置されています
   </a>
  </div>
  <div id="piyo" class="fuga">
   ここはdiv#piyo.fugaです。
   <a href="#">
    ここはaです。div#piyo.fuga直下に配置されています。
   </a>
   <p>
    ここはpです。div#piyo.fugaの中に配置されています。
    <a href="#">
     ここはaです。div#piyo.fuga内ですがその直下にはありません。
    </a>
    <a href="#">
     ここはaです。div#piyo.fuga内ですがその直下にはありません。
    </a>
   </p>
   <span class="fuga">
    ここはspan.fugaです。div#piyo.fugaの中に配置されています。
   </span>
   <p class="piyo">
    ここはp.piyoです。span.fugaと同じ高さでその直後に配置されています
   </p>
  </div>
  <p class="piyo">
   ここはp.piyoです。div#piyo.fugaと同じ高さでその直後に配置されています
  </p>
</body>
</html>

Elementセレクタ

Element(要素)とは?を先に説明しておきますと
<a href="#" >リンク</a>
このタグ開始~タグ終了セットをElementと呼んでいます。よくタグとエレメントがごっちゃになったりしますが、タグは開始タグも終了タグもタグですので、エレメントより分解された言い方ですね。『タグと要素(エレメント)の違いを説明して』がわかりやすいです。

で、ちょっと余談ですが、じゃあエレメントとノードの違いって何よ?って疑問があって、C#でパタパタ実装しているとXmlElementとXmlNodeなんてのを使うことになるんですが、XmlElementとXmlNodeというクラスの扱いだけで言うと、「りんご:XmlElement」と「フルーツ:XmlNode」くらいに覚えておけばいいみたいです。XmlNodeのほうが抽象化されてるってか一般化されてるって言うかそういう風に腹に落としました。ふーん。
で、javascriptのgetElementByIdだとエレメントが返るぞと言われてる気がするのですが、selectSingleNodeっていわれるとノードだと言われる。後者はXPathで指定するので手続きは違うとして、返って来たモノはなんだろね。呼び方の違い?まぁタイプセーフではないのでごちゃごちゃいわないことにします。(余談なげー)

ということでElementセレクタの説明です。
    Ext.query( "span" ); // [ span.fuga, span.fuga ]
このクエリは[ span.fuga, span.fuga ]という2つの要素からなる配列を返します。全ドキュメントからspanエレメントに合致するものを拾ってきます。
    Ext.query( "span", "fuga" ); // [ span.fuga ]
このクエリは[ span.fuga ]という1つの要素からなる配列を返します。サンプルHTMLにはspanエレメントが2つありますが、idが"fuga"からなるdiv内のspanを指定していますので、前者のほうが返ります。

次は書式付の第1パラメータで取得する方法です。

id指定:"#"

idを指定してエレメントを拾いたい場合は、"#"をプレフィックスとして与えてあげます。
    Ext.query( "#piyo" ); // [ div#piyo.fuga ]
このクエリは、idが"piyo"でclassが"fuga"のdivが返ります。2つあるdivのうち後者のほうが返ります。

class指定:"."

classを指定してエレメントを拾いたい場合は、"."をプレフィックスとして与えてあげます。
    Ext.query( ".piyo" ); // [ div#fuga.piyo, a.piyo, p.piyo, p.piyo ]
このクエリは、classが"piyo"のエレメントを返します。

全指定:"*"

全ての要素を拾いたい場合は"*"を指定することで拾えます。
    Ext.query( "*" ); // [ html, head, meta, body, div#fuga.piyo, span.fuga, a.piyo, div#piyo.fuga, a, p, a, a, span.fuga, p.piyo, p.piyo ]

子エレメント指定:" "(スペース)

あるエレメント内で指定した全ての子エレメントを拾う場合は、" "(スペース)を間にはさんで与えてあげます。
    Ext.query( "div p" ); // [ p, p.piyo ]
このクエリは親にdivエレメントを持つ p を返します。
    Ext.query( "div span" ); // [ span.fuga, span.fuga ]
このクエリは親にdivエレメントを持つspanエレメントを返すので、2つのspanが返ります。

直下の子エレメント指定:">"または"/"

あるエレメント内の直下の子エレメントを指定して拾う場合は">"または"/"を間にはさんで与えてあげます。
    Ext.query( "div/a" ); // [ a.piyo, a ]
div#fuga.piyo直下のa.piyoエレメントとdiv#piyo.fuga直下のaエレメントを返します。pエレメント内の2つのaエレメントは直下にないので返しません。

直後の同じ高さ(兄弟要素)のエレメント指定:"+"

あるエレメントと同じ高さにいるエレメントで、その直後にいるエレメントを拾う場合は"+"を間にはさんで与えてあげます。一応挙動も確認したのですが(あまり使いそうにないなーと思いながら)、ちょっと自信ないです。
    Ext.query( ".fuga+.piyo" ); // [ a.piyo, p.piyo, p.piyo ]
ちょっとわかりにくいですが、class="fuga"のエレメントと同じ高さにいて、その直後でclass="piyo"のエレメントを返しています。

後方の同じ高さ(兄弟要素)のエレメント指定:"+"

あるエレメントと同じ高さにいるエレメントで、その後方にいるエレメントを拾う場合は"~"を間にはさんで与えてあげます。
    Ext.query( "a~span" ); // [ span.fuga ]
これもちょっとわかりにくいですが、aと同じ高さにいるspan.fugaを拾っています。"+"と違い、拾ってきたspan.fugaはaの直後にいません。これも一応挙動も確認したのですが、かなり自信ないです。

ほかにもアトリビュートから指定してエレメントを取得したり出来るのですがまた次回に紹介します。

via : DomQueryのチュートリアル
via : APIDoc

About 2008年06月

2008年06月にブログ「mojalog labs ExtJS まとめ」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2008年05月です。

次のアーカイブは2008年12月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Powered by
Movable Type 3.35