PDA

View Full Version : Gridのセルの結合について



tomoya
8 Mar 2009, 12:11 AM
Gridのセルの結合はどのように記述したらよいでしょうか?
いわゆるrowspan、colspanです。
宜しくお願いします。

amanoman
9 Mar 2009, 12:46 AM
Gridのheaderなら、プラグインがあるみたいです。
http://extjs.com/forum/showthread.php?t=22337

tomoya
9 Mar 2009, 4:17 AM
Gridのheaderなら、プラグインがあるみたいです。
http://extjs.com/forum/showthread.php?t=22337

早速の返事、ありがとうございます。
データ部のセルの結合はできないということなんでしょうか??

amanoman
9 Mar 2009, 7:43 PM
早速の返事、ありがとうございます。
データ部のセルの結合はできないということなんでしょうか??
残念ながら、Ext 2.2 でcolspan,rowspanに相当する機能は無いみたいです。
過去のフォーラム(英語)にも同じ質問がありました。
http://extjs.com/forum/showthread.php?t=45439&highlight=colspan+grid

rowspanならば、ちょっと無理がありますが、grouping機能で代替する位しか思いつきません。
サンプル:grid view grouping
http://extjs.com/deploy/dev/examples/grid/grouping.html

だれか、この問題を解決するGridの拡張機能を知っている人いませんか:-/

yuki
9 Mar 2009, 9:03 PM
早速の返事、ありがとうございます。
データ部のセルの結合はできないということなんでしょうか??

基本機能としてはできないですね。

なので、プラグインを作ってみました(ただ、結構適当に作ったので実用性はあまりないかもです。あと、CellSelectionModelではエラーが発生します。EditorGridPanelでは確認してません。長めの文字列が入ると行の高さがおかしくなります。途中で力尽きてしまいました(:|)

デモページはこちら
(http://extjssamples-jp.googlecode.com/svn/trunk/grid/tablegridsample.html)
Ext.ux.Tabularize


Ext.ux.Tabularize = function(cfg){
cfg = cfg || {};
Ext.apply(this,cfg);
};

Ext.ux.Tabularize.prototype = {
RS: '__rowspan__',
CS: '__colspan__',

init: function(gp){
this.grid = gp;
var view = gp.getView();
var that = this;
var ar = view.afterRender;
view.afterRender = function(){
ar.call(this);
that.spanCols();
that.spanRows();
};

view.getRow = function(row){
return Ext.select('.x-grid3-row tr', false, this.mainBody.dom).item(row).dom;
};

view.findRowIndex = function(el){
var r = this.findRow(el);
var tr = this.fly(el).findParent('tr', this.rowSelectorDepth);

return r ? r.rowIndex + tr.rowIndex : false;
};

gp.on({
columnresize: {
fn: this.recalculateCellWidth,
scope: this
},
columnmove: {
fn: function(){
this.spanCols();
this.spanRows();
},
scope: this
},
sortchange: {
fn: function(){
this.spanCols();
this.spanRows();
},
scope: this
}
});
},

recalculateCellWidth: function(){
var grid = this.grid;
var cm = grid.getColumnModel();
Ext.select('.x-grid3-row', true, grid.getView().mainBody.dom).each(function(r){
Ext.select('.x-grid3-row-table',true,r.dom).setStyle({tableLayout:'auto'});
});

Ext.select('.x-grid3-row tr', true, grid.getView().mainBody.dom).each(function(r){
var k = 0;
Ext.select('.x-grid3-cell-inner', true, r.dom).each(function(c,t,i){
var colspan = c.parent().dom.colSpan;
var width = 0;
for(var j=0; j<colspan; j++){
width += cm.getColumnWidth(i+j+k);
}
k += j-1;
// c.setWidth(width);
c.parent().setWidth(width);
});
});
},

spanCols: function(){
var grid = this.grid;
Ext.select('.x-grid3-cell-inner:nodeValue('+this.CS+')', true, grid.getView().mainBody.dom).each(function(c){
var t = c.parent().prev();
t.dom.colSpan++;
c.parent().remove();
});
this.recalculateCellWidth();
},

spanRows: function(){
var grid = this.grid;
Ext.select('.x-grid3-cell-inner:nodeValue('+this.RS+')', true, grid.getView().mainBody.dom).each(function(c){
var row = c.findParent('tr',10,true);
var del = row.findParent('div',10,true);
var dest = del.prev();
var tbody = dest.child('tbody');
tbody.appendChild(row);
del.remove();
},this);

Ext.select('.x-grid3-cell-inner:nodeValue('+this.RS+')', true, grid.getView().mainBody.dom).each(function(c){
var row = c.findParent('tr',10,true);
var idx = 0;

var tgt = this.findCellInColumn(c.parent(),row.prev());
tgt.dom.rowSpan++;
tgt = tgt.child('div');
tgt.setHeight(tgt.getHeight()+c.getHeight());
},this);

Ext.select('.x-grid3-cell-inner:nodeValue('+this.RS+')', true, grid.getView().mainBody.dom).each(function(c){
c.parent().remove();
},this);
},

findCellInColumn: function(cell, target_row){
var current_row = cell.parent();
var idx = cell.dom.cellIndex;
var real_idx = idx;
current_row.select('td',true).each(function(c){
if(c.dom.cellIndex >= idx) return false;
real_idx += c.dom.colSpan - 1;
});

var span = 0;
var ret = 0;
target_row.select('td',true).each(function(c){
span += c.dom.colSpan -1
if(span + c.dom.cellIndex >= real_idx){
ret = c.dom.cellIndex;
return false;
}
});


if(target_row.child('td:nth('+(ret+1)+')')
.child('.x-grid3-cell-inner:nodeValue('+this.RS+')')){
return this.findCellInColumn(cell, target_row.prev());
}else{
return target_row.child('td:nth('+(ret+1)+')');
}
}
};

あと、グリッドの縦線表示と折り返し対策で以下のCSSが必要かもです

.x-grid3-row{
border-style: none solid none none;
}

.x-grid3-cell-inner{
border-color: #EDEDED;
border-style: none none solid solid;
border-width: 0 0 1px 1px;
padding: 2px 2px 2px 4px;
text-align: center;
overflow: auto;
white-space: normal;
}


rowspan/colspanの指定の方法については、サンプルコードの中身を見てください。

Ext.grid.GridPanel(というかGridView)の構造は、単純なTABLEタグではなく、グリッドの「行」のそれぞれが独立したDIVタグになっていて、そのDIVタグの中にTRを一つだけ持ったTABLEタグが埋め込まれていて、そのTABLEのTDが一つ一つのセルになっています(実際には、TDの中にもう一つDIVタグが埋め込まれていて、それが実際のセルになります)。

なので、colspanは比較的簡単に実装できるのですが、rowspanはちょっと面倒でした。

あと、これを本気で実装しようとすると、GridViewの中の色々なメソッドをいろいろと書き換えてあげる必要がでてきます(行・列・セルをget/setするための各種メソッドあたり)。

rowspan/colspanが必要となる場面にもよりますが、普通にDataViewを使って実現した方がいいかもしれません:)(GridViewの中のTemplateを使えば、Gridっぽい見た目にはできますし)

case-k
9 Mar 2009, 9:08 PM
基本的にはGridPanelは、マス目な「表」を表現する為だけ(?)にデザインされているようなので、自身で拡張などをしないといけないかもしれませんね。

例えば、sampleにあるfeed viewerのentryに対するsummary表示機能のように、GridView(GridPanelからならviewConfig)をいぢるとか。
http://extjs.com/deploy/dev/examples/feed-viewer/view.html

軽い気持ちで一つのcellに複数情報を表示したい時は、個人的には、templateをつかってColumnModelのrendererで表示データをいぢったりします。

===
と、書いてPOSTしたら・・・yukiさんのpluginがPOSTされてました(w
すばらしい!

tomoya
11 Mar 2009, 7:07 AM
amanomanさん、yukiさん、case-kさん

返信ありがとうございます!

セルの結合はどこでもあるような仕様だと思うので対応してくれてもいいのになとは思います。
javascriptをゴリゴリ書くんではなく、ライブラリでいかに楽できるかってことがミソだと思うんですけど。。

yuki
11 Mar 2009, 8:34 PM
セルの結合はどこでもあるような仕様だと思うので対応してくれてもいいのになとは思います。
javascriptをゴリゴリ書くんではなく、ライブラリでいかに楽できるかってことがミソだと思うんですけど。。

個人的には今のGridPanel/GridViewの仕組み的には難しいと思いますが、そういう要望が多ければ、対応すると思いますよ(過去にはSliderやHistoryあたりの機能がフォーラムからのリクエストで実装されましたし、3.0ではMenuがContainerのサブクラスになったり、DataViewとGridViewの中間のようなListViewも追加されますし)。

機能追加や仕様変更の要望は、「オープンディスカッション」板までぜひお願いします!:)
(ある程度まとまったら英語にして開発者にフィードバックしますので)