PDA

View Full Version : [2.0b1][SOLVED] Table layout problem with rowspan/colspan



jo2008
29 Oct 2007, 3:26 AM
Hi,
I think there's a problem with the table layout. I try to do a 3 by 4 table with elements that have rowspans and colspans defined. The layout is not generated correctly though. The 2nd line has 5 columns instead of 4.

I have the following piece of code:


layout: 'table',
layoutConfig: {
columns: 4
},
items: [
{
rowspan: 3,
html: "firstColumn"
},{
colspan: 3,
html: "firstRow"
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"Range", hideLabel: true,name:"radio1"})
},{
layout: 'form',
items: new Ext.form.NumberField({fieldLabel:"From",width:50})
},{
layout: 'form',
items: new Ext.form.NumberField({fieldLabel:"To",width:50})
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"List", hideLabel: true,name:"radio1"})
},{
colspan: 2,
layout: 'form',
items: [new Ext.form.TextField({width:100})]
}
]

This code is nested in the row of another table, but I don't think that's part of the problem..

Jo

jsakalos
30 Oct 2007, 12:25 PM
Would you please post a complete file(s) droppable into ext/examples tree?

brian.moeskau
2 Nov 2007, 6:48 AM
Hi jo2008,

Can you please drop this override into your code and let me know if it fixes the issue for you?



Ext.override(Ext.layout.TableLayout, {
spanCells : [],
getNextCell : function(c){
var td = document.createElement('td'), row;
if(!this.columns){
row = this.getRow(0);
}else {
if(this.currentColumn !== 0 && (this.currentColumn % this.columns === 0)){
row = this.getRow(++this.currentRow);
this.currentColumn = (c.colspan || 0) + (this.spanCells[this.currentRow] || 0);
}else{
row = this.getRow(this.currentRow);
this.currentColumn += (c.colspan || 1);
}
}
if(c.colspan){
td.colSpan = c.colspan;
}
if(c.cellId){
td.id = c.cellId;
}
td.className = 'x-table-layout-cell';
if(c.cellCls){
td.className += ' ' + c.cellCls;
}
if(c.rowspan){
td.rowSpan = c.rowspan;
var r = this.currentRow;
//track spanned cells to include in the column count later
for(i = r+1; i < r+c.rowspan; i++){
if(this.spanCells[i]){
this.spanCells[i] += 1;
}else{
this.spanCells[i] = 1;
}
}
}
row.appendChild(td);
return td;
}
});

jo2008
6 Nov 2007, 8:13 AM
Hi Brian,

after adding that code, the rowspan problem was fixed in one case where i hit the bug.

But then another error occured: The surrounding table (one column), that contains 3 tables as items, suddenly messed up its layout by putting the third table next to the second, although the layout config had columns set to 1.

Independently of this problem, the 3 inner tables didn't expand to the parent's width but only to their elements' width. I tried setting all kinds of style and bodystyle configs to 100% but none of them worked.

I removed this outer table and used the standard layout since this also has the effect that i wanted. The 3 inner tables are now using the full width.


I suspect that there is more wrong with the fix though. I tried to adapt another form of mine, consisting of 7 tables, all being items of a Ext.Panel. The tables all use simple rowspan / colspan layouts and are all about 4:3 cells.
These tables seem to be influencing each other since only some of them are layouted correctly. by editing the order or commenting out some of the tables, other tables' layouts are messed up that were ok before..

I really can't find any pattern behind this. I even tried copy/pasting one single table and some of the resulting layouts were ok, some were messed up.

The amount of columns does not exceed the setting anymore, but the messed up tables seem to finish the rows too early.


Jo

jo2008
6 Nov 2007, 8:25 AM
Heres the code that i use.. it's a function that returns a panel with all the elements.

Hope this will be enough to be able to reproduce the effect.

In my scenario, I put the panel in a Tab control which is inside a border layout of a window.


Thanks,

Jo

brian.moeskau
12 Nov 2007, 1:57 PM
Sorry for the delay. I have a patch that is fully tested and seems to work fine with your code. However, the patch code still needs a bit of refactoring before I post it. This was one of those things where the existing code worked great for 90% of scenarios, but getting those last 10% of scenarios covered was 90% of the effort. 8-|

brian.moeskau
13 Nov 2007, 12:12 AM
Can you please retest using this updated patch? Note that this code actually includes the patch plus an extensive test case. You should be able to drop this entire block into an empty test page and run it. If you find any problems, please provide a test case showing the issue.


Ext.onReady(function(){
Ext.override(Ext.layout.TableLayout, {
setContainer : function(ct){
Ext.layout.TableLayout.superclass.setContainer.call(this, ct);

this.currentRow = 0;
this.currentColumn = 0;
this.spanCells = [];
},
getNextCell : function(c){
var td = document.createElement('td'), row, colIndex;
if(!this.columns){
row = this.getRow(0);
}else {
colIndex = this.currentColumn;
if(colIndex !== 0 && (colIndex % this.columns === 0)){
this.currentRow++;
colIndex = (c.colspan || 1);
}else{
colIndex += (c.colspan || 1);
}

//advance to the next non-spanning row/col position
var cell = this.getNextNonSpan(colIndex, this.currentRow);
this.currentColumn = cell[0];
if(cell[1] != this.currentRow){
//we are on a new row
this.currentRow = cell[1];
if(c.colspan){
//since the col index is now set at the start of the
//new cell, any colspan needs to get reapplied. This is
//only necessary if the row changed since the col index
//only gets reset in that case
this.currentColumn += c.colspan - 1;
}
}
row = this.getRow(this.currentRow);
}
if(c.colspan){
td.colSpan = c.colspan;
}
if(c.cellId){
td.id = c.cellId;
}
td.className = 'x-table-layout-cell';
if(c.cellCls){
td.className += ' ' + c.cellCls;
}
if(c.rowspan){
td.rowSpan = c.rowspan;
var rowIndex = this.currentRow, colspan = c.colspan || 1;
//track rowspanned cells to include in the column count during the next call to getNextCell
for(var r = rowIndex+1; r < rowIndex+c.rowspan; r++){
for(var col=this.currentColumn-colspan+1; col <= this.currentColumn; col++){
if(!this.spanCells[col]){
this.spanCells[col] = [];
}
this.spanCells[col][r] = 1;
}
}
}
row.appendChild(td);
return td;
},
//private
getNextNonSpan: function(colIndex, rowIndex){
var c = (colIndex <= this.columns ? colIndex : this.columns), r = rowIndex;
for(var i=c; i <= this.columns; i++){
if(this.spanCells[i] && this.spanCells[i][r]){
if(++c > this.columns){
//exceeded column count, so reset to the start of the next row
return this.getNextNonSpan(1, ++r);
}
}else{
break;
}
}
return [c,r];
}
});

// TEST CASES:
var w = new Ext.Window({
title: 'Table Layout Tests',
height: 350,
width: 500,
border: false,
layout: 'fit',
items: [{
xtype:'tabpanel',
activeTab: 0,
enableTabScroll: true,
layoutOnTabChange: true,
defaults: {
bodyStyle: 'padding:10px;'
},
items: [{
title:'Complex Table Form',
layout:'table',
defaults: {
border:false,
bodyStyle:'padding:0 5px;'
},
layoutConfig: {
columns: 4
},
items: [{
colspan: 4,
html: "Header"
},{
rowspan: 3,
html: "firstColumn"
},{
colspan: 3,
html: "firstRow"
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"Range", hideLabel: true,name:"radio1",checked:true})
},{
layout: 'form',
labelWidth: 40,
items: new Ext.form.NumberField({fieldLabel:"From",width:50})
},{
layout: 'form',
labelWidth: 30,
items: new Ext.form.NumberField({fieldLabel:"To",width:50})
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"List:", hideLabel: true,name:"radio1"})
},{
colspan: 2,
layout: 'form',
labelWidth: 40,
items: [new Ext.form.TextField({width:145, hideLabel: true, anchor:'100%'})]
},{
colspan: 3,
html: "Second set of fields"
},{
rowspan: 2,
html: "Nested<br />rowspan"
},{
colspan: 3,
html: "With a Subtitle"
},{
rowspan: 3,
html: "firstColumn"
},{
colspan: 3,
html: "firstRow"
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"Range", hideLabel: true,name:"radio1",checked:true})
},{
layout: 'form',
labelWidth: 40,
items: new Ext.form.NumberField({fieldLabel:"From",width:50})
},{
layout: 'form',
labelWidth: 30,
items: new Ext.form.NumberField({fieldLabel:"To",width:50})
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"List:", hideLabel: true,name:"radio1"})
},{
colspan: 2,
layout: 'form',
labelWidth: 40,
items: [new Ext.form.TextField({width:145, hideLabel: true, anchor:'100%'})]
}]
},{
title:'Plain Grid',
layout:'table',
defaults: {
border:false,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 5
},
items: [
{html:'1,1'},{html:'1,2'},{html:'1,3'},{html:'1,4'},{html:'1,5'},
{html:'2,1'},{html:'2,2'},{html:'2,3'},{html:'2,4'},{html:'2,5'},
{html:'3,1'},{html:'3,2'},{html:'3,3'},{html:'3,4'},{html:'3,5'},
{html:'4,1'},{html:'4,2'},{html:'4,3'},{html:'4,4'},{html:'4,5'},
{html:'5,1'},{html:'5,2'},{html:'5,3'},{html:'5,4'},{html:'5,5'}
]
},{
title:'Rowspans',
layout:'table',
defaults: {
border:false,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 6
},
items: [
{html:'1,1',rowspan:4},{html:'1,2'},{html:'1,3'},{html:'1,4',rowspan:2},{html:'1,5'},{html:'1,6',rowspan:3},
{html:'2,2',rowspan:3},{html:'2,3'},{html:'2,5',rowspan:3},
{html:'3,3',rowspan:2},{html:'3,4'},
{html:'4,4'},{html:'4,5'}
]
},{
title:'Colspans',
layout:'table',
defaults: {
border:false,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 4
},
items: [
{html:'1,1',colspan:4},
{html:'2,1'},{html:'2,2'},{html:'2,3'},{html:'2,4'},
{html:'3,1'},{html:'3,2',colspan:3},
{html:'4,1',colspan:2},{html:'4,3',colspan:2},
{html:'5,1'},{html:'5,2',colspan:2},{html:'5,4'},
{html:'6,1',colspan:4}
]
},{
title:'Mixed Spanning',
layout:'table',
defaults: {
border:false,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 6
},
items: [
{html:'1,1',colspan:2},{html:'1,3',colspan:2,rowspan:2},{html:'1,5',rowspan:3},{html:'1,6',rowspan:5},
{html:'2,1',rowspan:3},{html:'2,2'},
{html:'3,2'},{html:'3,3'},{html:'3,4'},
{html:'4,2',colspan:2,rowspan:2},{html:'4,4'},{html:'4,5'},
{html:'5,1'},{html:'5,4',colspan:2},
{html:'6,1',colspan:6}
]
}]
}]
});
w.show();
});

jo2008
13 Nov 2007, 1:45 AM
Hi Brian,

I tested the patch both with your test case and with my existing code and both seem to be working fine. I compared your definitions and the resulting examples and I coulnd't find any errors. My table layout code was also rendered correctly now.
I can understand that even though the html tables seem to be very simple, the calculations behind them are brainwrecking.. So thanks alot for the great work and effort!

Jo

brian.moeskau
13 Nov 2007, 1:52 AM
Great! I'll go ahead and apply the fixes to SVN. Yes, this one was a bit of a pain :s -- but at least its fixed. :D

Let me know if any other issues crop up with the spanning.

jo2008
13 Nov 2007, 2:38 AM
While we're at it.. is it possible to vertical align the contents of table fields?

I'd like the rowspan>1 fields to display their contents at the top end.

I've tried setting the style/bodystyle config of the content panel to "vertical-align:top;". I also tried using that settign on the panel that has the table definition...

What prevents the vertical alignment?

Jo

brian.moeskau
13 Nov 2007, 3:36 AM
You need the alignment style to apply at the TD level for tables. The easiest approach would be to give the TableLayout's containing panel an id then add a CSS rule like this:


#my-panel td {
vertical-align:top;
}

jo2008
13 Nov 2007, 9:30 AM
That did it, thank you!


Will set to thread to resolved.

bagwg1127
11 Jan 2008, 9:17 PM
{
title:'Debug Spanning',
layout:'table',
defaults: {
border:false,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 3
},
items: [
{html:'1,1',rowspan:3},{html:'1,2'},{html:'1,3'},
{html:'2,2',colspan:2},
{html:'3,2'},{html:'3,3'}
]
}


try this one, bug still exists in 2.0 final

bagwg1127
12 Jan 2008, 4:07 AM
Ext.onReady(function(){
Ext.override(Ext.layout.TableLayout, {
setContainer : function(ct){
Ext.layout.TableLayout.superclass.setContainer.call(this, ct);

this.currentRow = 0;
this.currentColumn = 0;
this.spanCells = [];
},
getNextCell : function(c){
var td = document.createElement('td'), row, colIndex, nc;
if(!this.columns){
row = this.getRow(0);
}else {
colIndex = this.currentColumn;
if(colIndex !== 0 && (colIndex % this.columns === 0)){
this.currentRow++;
colIndex = 0;
nc = (c.colspan || 1);
}else{
nc = colIndex + (c.colspan || 1);
}

for(var ci = colIndex + 1, clen = nc; ci < clen; ci++) {
if(this.spanCells[ci] && this.spanCells[ci][this.currentRow + 1]) nc++;
}

colIndex = nc;

//advance to the next non-spanning row/col position
var cell = this.getNextNonSpan(colIndex, this.currentRow);
this.currentColumn = cell[0];
if(cell[1] != this.currentRow){
//we are on a new row
this.currentRow = cell[1];
if(c.colspan){
//since the col index is now set at the start of the
//new cell, any colspan needs to get reapplied. This is
//only necessary if the row changed since the col index
//only gets reset in that case
this.currentColumn += c.colspan - 1;
}
}
row = this.getRow(this.currentRow);
}
if(c.colspan){
td.colSpan = c.colspan;
}
if(c.cellId){
td.id = c.cellId;
}
td.className = 'x-table-layout-cell';
if(c.cellCls){
td.className += ' ' + c.cellCls;
}
if(c.rowspan){
td.rowSpan = c.rowspan;
var rowIndex = this.currentRow, colspan = c.colspan || 1;
//track rowspanned cells to include in the column count during the next call to getNextCell
for(var r = rowIndex+1; r < rowIndex+c.rowspan; r++){
for(var col=this.currentColumn-colspan+1; col <= this.currentColumn; col++){
if(!this.spanCells[col]){
this.spanCells[col] = [];
}
this.spanCells[col][r] = 1;
}
}
}
row.appendChild(td);
return td;
},
//private
getNextNonSpan: function(colIndex, rowIndex){
var c = (colIndex <= this.columns ? colIndex : this.columns), r = rowIndex;
for(var i=c; i <= this.columns; i++){
if(this.spanCells[i] && this.spanCells[i][r]){
if(++c > this.columns){
//exceeded column count, so reset to the start of the next row
return this.getNextNonSpan(1, ++r);
}
}else{
break;
}
}
return [c,r];
}
});

// TEST CASES:
var w = new Ext.Window({
title: 'Table Layout Tests',
height: 350,
width: 500,
border: false,
layout: 'fit',
items: [{
xtype:'tabpanel',
activeTab: 0,
enableTabScroll: true,
layoutOnTabChange: true,
defaults: {
bodyStyle: 'padding:10px;'
},
items: [{
title:'Complex Table Form',
layout:'table',
defaults: {
border:false,
bodyStyle:'padding:0 5px;'
},
layoutConfig: {
columns: 4
},
items: [{
colspan: 4,
html: "Header"
},{
rowspan: 3,
html: "firstColumn"
},{
colspan: 3,
html: "firstRow"
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"Range", hideLabel: true,name:"radio1",checked:true})
},{
layout: 'form',
labelWidth: 40,
items: new Ext.form.NumberField({fieldLabel:"From",width:50})
},{
layout: 'form',
labelWidth: 30,
items: new Ext.form.NumberField({fieldLabel:"To",width:50})
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"List:", hideLabel: true,name:"radio1"})
},{
colspan: 2,
layout: 'form',
labelWidth: 40,
items: [new Ext.form.TextField({width:145, hideLabel: true, anchor:'100%'})]
},{
colspan: 3,
html: "Second set of fields"
},{
rowspan: 2,
html: "Nested<br />rowspan"
},{
colspan: 3,
html: "With a Subtitle"
},{
rowspan: 3,
html: "firstColumn"
},{
colspan: 3,
html: "firstRow"
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"Range", hideLabel: true,name:"radio1",checked:true})
},{
layout: 'form',
labelWidth: 40,
items: new Ext.form.NumberField({fieldLabel:"From",width:50})
},{
layout: 'form',
labelWidth: 30,
items: new Ext.form.NumberField({fieldLabel:"To",width:50})
},{
layout: 'form',
items: new Ext.form.Radio({boxLabel:"List:", hideLabel: true,name:"radio1"})
},{
colspan: 2,
layout: 'form',
labelWidth: 40,
items: [new Ext.form.TextField({width:145, hideLabel: true, anchor:'100%'})]
}]
},{
title:'Plain Grid',
layout:'table',
defaults: {
border:true,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 5
},
items: [
{html:'1,1'},{html:'1,2'},{html:'1,3'},{html:'1,4'},{html:'1,5'},
{html:'2,1'},{html:'2,2'},{html:'2,3'},{html:'2,4'},{html:'2,5'},
{html:'3,1'},{html:'3,2'},{html:'3,3'},{html:'3,4'},{html:'3,5'},
{html:'4,1'},{html:'4,2'},{html:'4,3'},{html:'4,4'},{html:'4,5'},
{html:'5,1'},{html:'5,2'},{html:'5,3'},{html:'5,4'},{html:'5,5'}
]
},
{
title:'Rowspans',
layout:'table',
defaults: {
border:true,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 6
},
items: [
{html:'1,1',rowspan:4},{html:'1,2'},{html:'1,3'},{html:'1,4',rowspan:2},{html:'1,5'},{html:'1,6',rowspan:3},
{html:'2,2',rowspan:3},{html:'2,3'},{html:'2,5',rowspan:3},
{html:'3,3',rowspan:2},{html:'3,4'},
{html:'4,4'},{html:'4,6'}
]
},{
title:'Colspans',
layout:'table',
defaults: {
border:true,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 4
},
items: [
{html:'1,1',colspan:4},
{html:'2,1'},{html:'2,2'},{html:'2,3'},{html:'2,4'},
{html:'3,1'},{html:'3,2',colspan:3},
{html:'4,1',colspan:2},{html:'4,3',colspan:2},
{html:'5,1'},{html:'5,2',colspan:2},{html:'5,4'},
{html:'6,1',colspan:4}
]
},{
title:'Mixed Spanning',
layout:'table',
defaults: {
border:true,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 6
},
items: [
{html:'1,1',colspan:2},{html:'1,3',colspan:2,rowspan:2},{html:'1,5',rowspan:3},{html:'1,6',rowspan:5},
{html:'2,1',rowspan:3},{html:'2,2'},
{html:'3,2'},{html:'3,3'},{html:'3,4'},
{html:'4,2',colspan:2,rowspan:2},{html:'4,4'},{html:'4,5'},
{html:'5,1'},{html:'5,4',colspan:2},
{html:'6,1',colspan:6}
]
},
{
title:'Debug Spanning',
layout:'table',
defaults: {
border:true,
bodyStyle:'padding:5px 15px;'
},
layoutConfig: {
columns: 3
},
items: [
{html:'1,1',rowspan:3},{html:'1,2'},{html:'1,3'},
{html:'2,2',colspan:2},
{html:'3,2'},{html:'3,3'}
]
}
]
}]
});
w.show();
});

myExtJsUname
6 Feb 2009, 11:58 AM
I realize this is a very old post but just in case someone comes across this post because of the last question regarding the vertical alignment in the cells, I got the vertical alignment to work by setting the cellCls of whatever item I was adding to the cell to "somename" and in my css creating:

.somename {
vertical-align: top;
}