原创,转载请标明出处。
Extjs自带的FormLayout布局若要使用多列,需要自己做tpl或者使用HTML+renderTo,很麻烦。而TableLyout只支持Flow排列,且用于FormPanel中不能正确显示Label,等等,两者皆不适合自由的table+form布局需求。
FormTablLayout将解决该问题。
My.layout.FormTableLayout
= FormLayout + TableLayout
增强项:可指定显示位置
….
等
.
使用方法:适用于
FormLayout/TableLayout
所适用场合,指定
layout
为
formtable
即可。
1)
基本功能:
a)
指定表格列数
(
行数将根据内容自动添加
)
b)
横、纵向融合
c)
一个单元格内容
d)
在一个单元格中显示多项内容
e)
指定显示位置
f)
支持显示
form field
g)
单元格内容显示模板可设置
2)
使用范例
var
items=[
{
html:'
横向融合
',
row:3,
col:0,
colspan:2
},{
html:'
-------
我的地盘
--------
',
row:2,
col:1
},{
html:'========
我也来
===========',
row:2,
col:1
}……
var
tableForm=new
Ext.FormPanel({
layout:
'formtable'
,
//
使用
FormTableLayout
style:
'height:100%',
title:
'Table
Form',
items:
items
});
3)
效果
/*
*
* @class My.ui.FormTableLayout
* @extends Ext.layout.ContainerLayout
* @author zfzheng
*
*
*
* FormTableLayout= FormLayout + TableLayout
*
* 适用于FormLayout/TableLayout适用的场合。
* 除拥有FormLayout/TableLayout特征外,还具有指定单元格显示功能:
* 设置autoFlow为true,根据items项的row/col属性定位单元格排列。
* 可以在同一单元格内显示多项item(指定row/col)为同一位置即可,按在原数组中的顺序排列。
* 注:
* 1)若autoFlow为true,items项没有row或col属性的均不被显示。
* 2)若某item的row/col设置在被融合了的单元格位置,则该项不被显示。
*
var items=[
{
html:'横向融合',
row:3,
col:0,
colspan:2
},{
html:'-------我的地盘--------',
row:2,
col:1
},{
html:'========我也来===========',
row:2,
col:1
},{
html:'~~~~~~~~~真挤啊~~~~~~~~~~',
row:2,
col:1
},{
html:'非第一单元格起填充',
row:0,
col:1
},{
html:'纵向融合',
row:1,
col:0,
rowspan:2
},{
html:'Hello,how are you?正常Cell',
row:1,
col:1
},{
name:'count',
fieldLabel:'件数',
value:'1',
xtype:'numberfield',
row:4,
col:0
},{
name:'logTime',
fieldLabel:'编制日期',
value:'',
xtype:'datefield',
row:4,
col:1
}
]
var tableForm=new Ext.FormPanel({
layout: 'formtable', //使用FormTableLayout
style: 'height:100%',
title: 'Table Form',
items: items
});
tableForm.renderTo(Ext.getBody());
*
*
*/
My.layout.FormTableLayout = Ext.extend(Ext.layout.ContainerLayout,{
// private
monitorResize: false ,
/* *
* Table列数
*/
columns: 2 ,
/* *
* 是否自动排列
* 若为true,则按行方向自动排列。
* 若为false,则根据item指定的[row,col]排列,无row/col属性的项被忽略显示。
*/
autoFlow: false ,
/* *
* Form Element显示模板
*/
fieldTpl:
new Ext.Template(
' <div class="x-form-item {5}" tabIndex="-1"> ' ,
' <label for="{0}" style="{2}" class="x-form-item-label">{1}{4}</label> ' ,
' <div class="x-form-element" id="x-form-el-{0}" style="{3}"> ' ,
' </div><div class="{6}"></div> ' ,
' </div> '
),
/* *
* 普通内容(非Form Element)显示模板
*/
cellContentTpl: new Ext.Template( ' <div class="x-form-item-label">{0}</div> ' ),
// private
setContainer : function (ct){
My.layout.FormTableLayout.superclass.setContainer.call( this , ct);
this .currentRow = 0 ;
this .currentColumn = 0 ;
this .spanCells = [];
this .maxCreatedRowIndex =- 1 ;
},
// private
onLayout : function (ct, target){
var cs = ct.items.items, len = cs.length;
if ( ! this .table){
// target.addClass('x-table-layout-ct');
this .table = target.createChild(
{tag: ' table ' , cls: ' x-table-layout ' , border: 1 , cellspacing: 3 , cn: {tag: ' tbody ' }}, null , true );
if ( ! this .autoFlow){
var items = [];
for ( var i = 0 ;i < len;i ++ ){
// 如果设置了autoFlow为true,则无row、col属性的字段将无法显示。
if ( typeof cs[i].row != ' undefined ' && typeof cs[i].col != ' undefined ' ){
cs[i].__index = i;
items.push(cs[i]);
}
}
ct.items.items = items.sort( function (item1,item2){
if (item1.row == item2.row){
if (item1.col == item2.col){
return item1.__index - item2.__index;
}
return item1.col - item2.col;
}
return item1.row - item2.row;
});
}
this .renderAll(ct, target);
}
},
// private
createRow : function (index){
var row = this .table.tBodies[ 0 ].childNodes[index];
if ( ! row){
row = document.createElement( ' tr ' );
this .table.tBodies[ 0 ].appendChild(row);
this .maxCreatedRowIndex = Math.max( this .maxCreatedRowIndex,index);
}
return row;
},
// private
getRow : function (index){
if ( ! this .autoFlow){
// 确保创建index之前的row
for ( var i = Math.max( 0 , this .maxCreatedRowIndex + 1 );i < index;i ++ ){
this .createRow(i);
}
}
return this .createRow(index);
},
// private
getCellAt : function (c,row,col){
if ( this .spanCells[col] && this .spanCells[col][row]){
return null ;
}
// 计算之前有多少spanCells
var spanCount = 0 ;
for ( var i = 0 ;i < col;i ++ ){
if ( this .spanCells[i] && this .spanCells[i][row]){
spanCount ++ ;
}
}
var r = this .getRow(row);
var cell = r.childNodes[col - spanCount];
if (cell){
return cell;
}
this .currentRow = row;
// 预设置nextCell之前的初始列索引
this .currentColumn = spanCount - 1 ;
for ( var i = spanCount;i <= col;i ++ ){
this .getNextCell(c);
}
return r.childNodes[col - spanCount];
},
// private
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){
this .currentRow = cell[ 1 ];
if (c.colspan){
this .currentColumn += c.colspan - 1 ;
}
}
row = this .getRow( this .currentRow);
}
if (c.colspan){
td.colSpan = c.colspan;
}
td.className = ' x-table-layout-cell ' ;
if (c.rowspan){
td.rowSpan = c.rowspan;
var rowIndex = this .currentRow, colspan = c.colspan || 1 ;
// track rowspanned cells to add to the column index 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];
},
// private
renderItem : function (c, position, target){
// target is form or other container
if (c && ! c.rendered ){
var td = this .autoFlow ? this .getNextCell(c): this .getCellAt(c,c.row,c.col);
if ( ! td){
// cell at[row,col] is spanCell,不渲染。(row,col设置不正确)
return ;
}
if (c.isFormField && c.inputType != ' hidden ' ){
var args = [
c.id, c.fieldLabel,
c.labelStyle || this .labelStyle || '' ,
this .elementStyle || '' ,
typeof c.labelSeparator == ' undefined ' ? this .labelSeparator : c.labelSeparator,
(c.itemCls || this .container.itemCls || '' ) + (c.hideLabel ? ' x-hide-label ' : '' ),
c.clearCls || ' x-form-clear-left '
];
if ( typeof position == ' number ' ){
position = target.dom.childNodes[position] || null ;
}
if (position){
// this.fieldTpl.insertBefore(position, args);
this .fieldTpl.append(td, args);
} else {
// this.fieldTpl.append(target, args);
this .fieldTpl.append(td, args);
}
c.render( ' x-form-el- ' + c.id);
} else {
// My.layout.FormTableLayout.superclass.renderItem.apply(this, arguments);
this .cellContentTpl.append(td,[c.text || c.html || c.value]);
}
}
},
// private
isValidParent : function (c, target){
return true ;
}
});
Ext.Container.LAYOUTS[ ' formtable ' ] = My.layout.FormTableLayout;
* @class My.ui.FormTableLayout
* @extends Ext.layout.ContainerLayout
* @author zfzheng
*
*
*
* FormTableLayout= FormLayout + TableLayout
*
* 适用于FormLayout/TableLayout适用的场合。
* 除拥有FormLayout/TableLayout特征外,还具有指定单元格显示功能:
* 设置autoFlow为true,根据items项的row/col属性定位单元格排列。
* 可以在同一单元格内显示多项item(指定row/col)为同一位置即可,按在原数组中的顺序排列。
* 注:
* 1)若autoFlow为true,items项没有row或col属性的均不被显示。
* 2)若某item的row/col设置在被融合了的单元格位置,则该项不被显示。
*
var items=[
{
html:'横向融合',
row:3,
col:0,
colspan:2
},{
html:'-------我的地盘--------',
row:2,
col:1
},{
html:'========我也来===========',
row:2,
col:1
},{
html:'~~~~~~~~~真挤啊~~~~~~~~~~',
row:2,
col:1
},{
html:'非第一单元格起填充',
row:0,
col:1
},{
html:'纵向融合',
row:1,
col:0,
rowspan:2
},{
html:'Hello,how are you?正常Cell',
row:1,
col:1
},{
name:'count',
fieldLabel:'件数',
value:'1',
xtype:'numberfield',
row:4,
col:0
},{
name:'logTime',
fieldLabel:'编制日期',
value:'',
xtype:'datefield',
row:4,
col:1
}
]
var tableForm=new Ext.FormPanel({
layout: 'formtable', //使用FormTableLayout
style: 'height:100%',
title: 'Table Form',
items: items
});
tableForm.renderTo(Ext.getBody());
*
*
*/
My.layout.FormTableLayout = Ext.extend(Ext.layout.ContainerLayout,{
// private
monitorResize: false ,
/* *
* Table列数
*/
columns: 2 ,
/* *
* 是否自动排列
* 若为true,则按行方向自动排列。
* 若为false,则根据item指定的[row,col]排列,无row/col属性的项被忽略显示。
*/
autoFlow: false ,
/* *
* Form Element显示模板
*/
fieldTpl:
new Ext.Template(
' <div class="x-form-item {5}" tabIndex="-1"> ' ,
' <label for="{0}" style="{2}" class="x-form-item-label">{1}{4}</label> ' ,
' <div class="x-form-element" id="x-form-el-{0}" style="{3}"> ' ,
' </div><div class="{6}"></div> ' ,
' </div> '
),
/* *
* 普通内容(非Form Element)显示模板
*/
cellContentTpl: new Ext.Template( ' <div class="x-form-item-label">{0}</div> ' ),
// private
setContainer : function (ct){
My.layout.FormTableLayout.superclass.setContainer.call( this , ct);
this .currentRow = 0 ;
this .currentColumn = 0 ;
this .spanCells = [];
this .maxCreatedRowIndex =- 1 ;
},
// private
onLayout : function (ct, target){
var cs = ct.items.items, len = cs.length;
if ( ! this .table){
// target.addClass('x-table-layout-ct');
this .table = target.createChild(
{tag: ' table ' , cls: ' x-table-layout ' , border: 1 , cellspacing: 3 , cn: {tag: ' tbody ' }}, null , true );
if ( ! this .autoFlow){
var items = [];
for ( var i = 0 ;i < len;i ++ ){
// 如果设置了autoFlow为true,则无row、col属性的字段将无法显示。
if ( typeof cs[i].row != ' undefined ' && typeof cs[i].col != ' undefined ' ){
cs[i].__index = i;
items.push(cs[i]);
}
}
ct.items.items = items.sort( function (item1,item2){
if (item1.row == item2.row){
if (item1.col == item2.col){
return item1.__index - item2.__index;
}
return item1.col - item2.col;
}
return item1.row - item2.row;
});
}
this .renderAll(ct, target);
}
},
// private
createRow : function (index){
var row = this .table.tBodies[ 0 ].childNodes[index];
if ( ! row){
row = document.createElement( ' tr ' );
this .table.tBodies[ 0 ].appendChild(row);
this .maxCreatedRowIndex = Math.max( this .maxCreatedRowIndex,index);
}
return row;
},
// private
getRow : function (index){
if ( ! this .autoFlow){
// 确保创建index之前的row
for ( var i = Math.max( 0 , this .maxCreatedRowIndex + 1 );i < index;i ++ ){
this .createRow(i);
}
}
return this .createRow(index);
},
// private
getCellAt : function (c,row,col){
if ( this .spanCells[col] && this .spanCells[col][row]){
return null ;
}
// 计算之前有多少spanCells
var spanCount = 0 ;
for ( var i = 0 ;i < col;i ++ ){
if ( this .spanCells[i] && this .spanCells[i][row]){
spanCount ++ ;
}
}
var r = this .getRow(row);
var cell = r.childNodes[col - spanCount];
if (cell){
return cell;
}
this .currentRow = row;
// 预设置nextCell之前的初始列索引
this .currentColumn = spanCount - 1 ;
for ( var i = spanCount;i <= col;i ++ ){
this .getNextCell(c);
}
return r.childNodes[col - spanCount];
},
// private
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){
this .currentRow = cell[ 1 ];
if (c.colspan){
this .currentColumn += c.colspan - 1 ;
}
}
row = this .getRow( this .currentRow);
}
if (c.colspan){
td.colSpan = c.colspan;
}
td.className = ' x-table-layout-cell ' ;
if (c.rowspan){
td.rowSpan = c.rowspan;
var rowIndex = this .currentRow, colspan = c.colspan || 1 ;
// track rowspanned cells to add to the column index 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];
},
// private
renderItem : function (c, position, target){
// target is form or other container
if (c && ! c.rendered ){
var td = this .autoFlow ? this .getNextCell(c): this .getCellAt(c,c.row,c.col);
if ( ! td){
// cell at[row,col] is spanCell,不渲染。(row,col设置不正确)
return ;
}
if (c.isFormField && c.inputType != ' hidden ' ){
var args = [
c.id, c.fieldLabel,
c.labelStyle || this .labelStyle || '' ,
this .elementStyle || '' ,
typeof c.labelSeparator == ' undefined ' ? this .labelSeparator : c.labelSeparator,
(c.itemCls || this .container.itemCls || '' ) + (c.hideLabel ? ' x-hide-label ' : '' ),
c.clearCls || ' x-form-clear-left '
];
if ( typeof position == ' number ' ){
position = target.dom.childNodes[position] || null ;
}
if (position){
// this.fieldTpl.insertBefore(position, args);
this .fieldTpl.append(td, args);
} else {
// this.fieldTpl.append(target, args);
this .fieldTpl.append(td, args);
}
c.render( ' x-form-el- ' + c.id);
} else {
// My.layout.FormTableLayout.superclass.renderItem.apply(this, arguments);
this .cellContentTpl.append(td,[c.text || c.html || c.value]);
}
}
},
// private
isValidParent : function (c, target){
return true ;
}
});
Ext.Container.LAYOUTS[ ' formtable ' ] = My.layout.FormTableLayout;