表格控件GridPanel
表格由类Ext.grid.GridPanel定义,继承自Panel,xtype为grid。在Ext中,表格控件Grid必须包含列定义信息,并制定表格的数据存储器Store。表格的列信息由类Ext.grid.ColumnModel定义,而表格的数据存储器由Ext.data.Store定义,数据存储器根据解析的数据的不同,可分为JsonStore、SimpleStore、GroupingStore等。
Grid需要进行哪些配置呢?
首先,表格是二维的,跟数据库表一样。我们需要设置表的列数、每列的名称和类型,以及如何显示。
在ext中,列的定义叫做ColumnModel,cm是它的简称。它作为表格的列模型,是应该首先创建的。
下面创建一个包含3列的Grid,第1列编号(id),第二列是名称(name),第3列是描述(description)。
var cm = new Ext.grid.ColumnModel({ {header:'编号',dataIndex:'id'}, {header:'名称',dataIndex:'name'}, {header:'描述',dataIndex:'description'} }); |
Var cm = new Ext.grid.ColumnModel(…)负责创建表格的列信息。表格包含的列由columns配置属性来描述,简称cm。columsn是一个数组,每一行数据元素描述表格的一列信息,表格的列信息包含首部显示文本(header)、列对应的记录集字段(dataIndex)、列是否可以排序(sortable)、列的渲染函数(renderer)、宽度(width)、格式化信息(format)等。上面的代码中只用到的header和dataIndex。
表格是二维的,数据自然也应该是二维的,下面用一个数组定义数据。
var data = [ ['1','name1','desc1'], ['2','name2','desc2'], ['3','name3','desc3'], ['4','name4','desc4'], ['5','name5','desc5'] ]; |
此二维数据中,每一维都是一行数据。为了将数据展示出来,还需要做转换。
var store = new Ext.data.Store({ proxy:new Ext.data.MemoryProxy(data), reader:new Ext.data.ArrayReader({},[ {name:'id'}, {name:'name'}, {name:'description'}, ]), });
store.load(); |
Varstore=…用来创建一个数据存储对象,这是GridPanel必须配置的属性。数据存储对象负责把各种各样的原始数据(如二维数组、Json对象数组、XML文本等)转换成Ext.data.Record类型的对象。通过Ext.data.Store,可以把任何格式的数据转换成Grid可以使用的形式,这样就不需要为每种数据格式写一个对应的实现。
Store对应两个部分:proxy和reader。Proxy是指获取数据的方式。Reader是指如何解析这些数据。这里使用的是Ext.data.MemoryProxy,它是专门用来解析javascript变量的。在定义MemoryProxy对象时,只需要将上面定义的data作为参数传递进去即可。
Ext.data.ArrayReader专门用来解析数组,并且告诉我们它会按照定义的规范进行解析,定义3个名称:id、name和description。再看前面cm定义的地方,这里的3个名称就和cm李的dataIndex相对应。这样,cm就知道column是如何与store中的数据相对应。需注意,要执行一次store.load(),以对数据进行初始化。
如果第1列显示的不是id而是name,第2列显示的不是name而是id,那么可以通过mapping来指定。
var store = new Ext.data.Store({
proxy:newExt.data.MemoryProxy(data),
reader:newExt.data.ArrayReader({},[
{name:'id',mapping:1},
{name:'name',mapping:0},
{name:'description',mapping:2},
]),
});
表格的列模型创建好了,原始数据与数据的转换也已经完成,剩下的就是把他们组装到一起,这样我们的Grid就创建好了。
var grid1 = newExt.grid.GridPanel({
title:'员工信息',
renderTo:'grid1',
cm:cm,
store:store,
width:500,
height:350
});
显示结果如下图:
11.2列拖放和改变列宽
默认情况下,是可以拖放列、改变列的宽度的。如果要取消则将enableColumnMore和enableColumnResize设置为false即可。
var grid1 = new Ext.grid.GridPanel({
title:'员工信息',
renderTo:'grid1',
cm:cm,
store:store,
width:500,
height:350,
enableColumnMove:false,
enableColumnResize:false
});
11.3遮罩和提示
Grid还支持一种遮罩和提示功能,设置loadMask为true,在store.load()完成之前,会一直显示“loading…”。
具体实现:
var grid1 = newExt.grid.GridPanel({
title:'员工信息',
renderTo:'grid1',
cm:cm,
store:store,
width:500,
height:350,
enableColumnMove:false,
enableColumnResize:false,
loadMask:true
});
store.load();
11.4为表格中的列指定宽度
上面的表格中每列的宽度都是一样的,我们可以自己指定每列的宽度(如果不指定,则宽度是默认值100px)。
var cm = newExt.grid.ColumnModel([
{header:'编号',dataIndex:'id',width:50},
{header:'名称',dataIndex:'name',width:100},
{header:'描述',dataIndex:'description',width:200}
]);
11.5自动扩展列1
这样还是比较麻烦,需要自己计算每列宽度,如果想让每列自动填满整个表格,可以使用viewConfig中的forceFit即可。它是给GridView用的配置,它在视图层会重新计算所有列宽后填充Grid。使用了forceFit后,Grid会根据cm里面的宽度按比例分配。
var grid1 = newExt.grid.GridPanel({
title:'员工信息',
renderTo:'grid1',
cm:cm,
store:store,
width:500,
height:350,
enableColumnMove:false,
enableColumnResize:false,
loadMask:true,
viewConfig:{
forceFit:true
}
});
11.6自动扩展列2
除了使用forceFit外,还可以考虑使用autoExpandColumn,它会自动扩展某列,从而填充整个表格。autoExpandColumn只能指定一列的ID,因此假设扩展description列,则需要在cm中指定id。
var cm = newExt.grid.ColumnModel([
{header:'编号',dataIndex:'id',width:50},
{header:'名称',dataIndex:'name',width:100},
{id:'descn',header:'描述',dataIndex:'description',width:200}
]);
var grid1 = newExt.grid.GridPanel({
title:'员工信息',
renderTo:'grid1',
cm:cm,
store:store,
width:500,
height:350,
enableColumnMove:false,
enableColumnResize:false,
loadMask:true,
/* viewConfig:{
forceFit:true
} */
autoExpandColumn:'descn'
});
11.7让表格支持排序
Ext中可以很方便的实现排序功能,只需要在定义列模型时,指定sortable为true即可。
var cm = newExt.grid.ColumnModel([
{header:'编号',dataIndex:'id',width:50},
{header:'名称',dataIndex:'name',width:100,sortable:true},
{id:'descn',header:'描述',dataIndex:'description',width:200}
]);
这样名称这一列就支持排序了。
结果如图:
11.8解决中文排序问题
Ext默认是用ASCII进行排序,我们却按拼音排序,因此Ext自动排好的中文在我们看来一团糟。
如何解决呢?我们可以通过一个sortInfo来为Ext.data.Store设置一个默认的排序方式。
var store = newExt.data.Store({
proxy:newExt.data.MemoryProxy(data),
reader:newExt.data.ArrayReader({},[
{name:'id',mapping:0},
{name:'name',mapping:1},
{name:'description',mapping:2},
]),
sortInfo:{field:'name',direction:'ASC'}
});
Field:指定排序的列,direction:指定排序方式,ASC升序,DESC降序。
这样排序看起来还是乱套的。
为了实现正确的中文排序, 我们需要自己重写Ext.data.Store的applySort()函数。
// 让Ext支持中文排序
Ext.data.Store.prototype.applySort = function() {
if( this.sortInfo && !this.remoteSort) {
var s = this.sortInfo,f =s.field;
var st = this.fields.get(f).sortType;
var fn = function(r1,r2) {
var v1 = st(r1,data[f]),v2 = st(r2.data[f]);
if (typeof(v1) == "string") {
returnv1.localeCompare(v2);
}
return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
};
this.data.sort(s.direction,fn);
if (this.snapshot&& this.snapshot != this.data) {
this.snapshot.sort(s.direction,fn);
}
}
}
将这段代码放到ext-all.js或放到页面的最前面,反正在ext初始化后,调用实际代码前就可以了。
11.9显示日期类型数据
完整的代码:
var cm = newExt.grid.ColumnModel([
{header:'编号',dataIndex:'id',width:50},
{header:'名称',dataIndex:'name',width:100,sortable:true},
{header:'生日',dataIndex:'birthday',width:100,sortable:true,
/* renderer:new Ext.util.Format.dateRenderer('y-m-d') */
renderer:function(value) {
if (value instanceof Date) {
returnnew Date(value).format('y-m-d');
}
return value;
}
},
{id:'descn',header:'描述',dataIndex:'description',width:200}
]);
var data = [
['1','啊','desc1','2000-01-15'],
['2','得','desc2','2010-01-15'],
['3','歌','desc3','2006-01-15'],
['4','波','desc4','2009-01-15'],
['5','此','desc5','2002-01-15']
];
var store = newExt.data.Store({
proxy:newExt.data.MemoryProxy(data),
reader:newExt.data.ArrayReader({},[
{name:'id'},
{name:'name'},
{name:'description'},
{name:'birthday'}
]),
sortInfo:{field:'name',direction:'ASC'}
});
var grid1 = newExt.grid.GridPanel({
title:'员工信息',
renderTo:'grid1',
cm:cm,
store:store,
width:500,
height:350,
enableColumnMove:false,
enableColumnResize:true,
loadMask:true,
/* viewConfig:{
forceFit:true
} */
autoExpandColumn:'descn'
});
store.load();
在上面的代码中,我加了一列birthday,本来按照文章中的说法,
在ColumnModel中birthday列加
type:’date’,renderer:newExt.util.Format.DateRenderer(‘y-m-d’),
在Store中的birthday列增加type:’date’,dateFormat:’y-m-d’都不行,没法显示birthday。
然后在往上看到Ext日期格式化的问题,我就想着还不如在后台就格式化好,然后传递一个字符串到页面,直接显示。但是如果是可编辑的列,即日期可以编辑,这样就有问题了,显示的又不对。那么综合起来,就像上面的代码写的,重新写了renderer,如果是Date型就格式化,否则就直接返回。
11.10在表格中显示红色的字、图片和按钮
在上面的代码基础上,增加了性别这一列,让男性显示为红色,女性显示为绿色。其实,很简单,通过自定义renderer就可以实现了。
{
header : '性别',
dataIndex : 'sex',
sortable : true,
renderer : function(value) {
if (value == 'man') {
return'<spanstyle="color:red;font-weight:bold">'+ value + '</span>';
}
return'<spanstyle="color:green;font-weight:bold">'+ value + '</span>';
}
}
结果:
在性别前加图片,这个更容易了。
{
header : '性别',
dataIndex : 'sex',
sortable : true,
renderer : function(value) {
if (value == 'man') {
return'<spanstyle="color:red;font-weight:bold"><img src=”../imgs/man.png”/>'+ value + '</span>';
}
return'<spanstyle="color:green;font-weight:bold"><img src=”../imgs/woman.png”/>'+ value + '</span>';
}
}
我们可以在renderer中得到多个参数,如下所示:
Value:要显示的单元格的值。
Cellmeta:单元格的相关属性,主要是Id和Css。
Record:这一行的数据对象,要获取某个列的值,可以通过record.data[“name”]这样获取。
rowIndex:行号,这里的行号是所有记录的顺序。
columnIndex:列号。
Store:创建表格时传递的数据存储器,通过它可以获取表格所有的数据。
参数的顺序:
renderer:function(value,cellmeta,record,rowIndex,columnIndex,store) {
}
11.11为表格的奇偶行设置不同的颜色
在viewConfig中增加getRowClass配置项,用它来处理。
var grid1 = newExt.grid.GridPanel({
title : '员工信息',
renderTo : 'grid1',
cm : cm,
store : store,
width : 700,
height : 350,
enableColumnMove : false,
enableColumnResize : true,
loadMask : true,
viewConfig:{
forceFit:true,
getRowClass:function(record,rowIndex,p,ds){
if (rowIndex % 2 ==0) {
return'oushu-row-color';
}
return'jishu-row-color';
}
},
autoExpandColumn : 'descn'
});
CSS:
<style>
.jishu-row-color{
background-color:white;
}
.oushu-row-color {
background-color:yellow;
}
</style>
结果:
11.12自动显示行号
只需要在ColumnModel中加入一行代码就可以了。
还存在一个问题,如果删除表格中的数据,行号就不连续了。那么我们就需要刷新Grid视图,让Grid重新计算行号。
我们在表格上添加一个右键菜单,用它来执行删除操作。
// 添加右键菜单。
grid1.addListener('rowcontextmenu', function(grid, rowIndex, e) {
e.preventDefault();
var menu = newExt.menu.Menu({
id : Ext.id(),
items : [ {
id : Ext.id(),
text : '<span font-weight:"BOLD">删除人员信息</span>',
handler : function() {
store.remove(store.getAt(rowIndex));// 删除对应行的数据
grid1.view.refresh(); //刷新视图,重新计算行号
}
}
]
});
menu.showAt(e.getXY());
});
11.13显示复选框
在ColumnModel中和Grid中增加CheckboxSelectionModel即可。
// 复选框
var sm = newExt.grid.CheckboxSelectionModel();
var cm = newExt.grid.ColumnModel(
[
new Ext.grid.RowNumberer(),
sm,
{
header : '编号',
dataIndex : 'id',
width : 50
},
{
header : '名称',
dataIndex : 'name',
width : 100,
sortable : true
},
{
header : '生日',
dataIndex : 'birthday',
width : 100,
sortable : true,
/* renderer:new Ext.util.Format.dateRenderer('y-m-d') */
renderer : function(value) {
if (value instanceof Date) {
returnnew Date(value).format('y-m-d');
}
return value;
}
},
{
header : '性别',
dataIndex : 'sex',
sortable : true,
renderer : function(value) {
if (value == 'man') {
return'<spanstyle="color:red;font-weight:bold">'+ value + '</span>';
}
return'<span style="color:green;font-weight:bold">'+ value + '</span>';
}
}, {
id: 'descn',
header: '描述',
dataIndex: 'description',
width: 200
} ]);
var grid1 = newExt.grid.GridPanel({
title : '员工信息',
renderTo : 'grid1',
cm : cm,
store : store,
width : 700,
height : 350,
enableColumnMove : false,
enableColumnResize : true,
loadMask : true,
viewConfig:{
forceFit:true,
getRowClass:function(record,rowIndex,p,ds){
if (rowIndex %2 == 0) {
return'oushu-row-color';
}
return'jishu-row-color';
}
},
autoExpandColumn : 'descn',
sm:sm
});
结果:
Grid里面提供的这种功能称为行选择模型,单击某个单元格时,选中的是整行,Ext默认的是RowSelectionModel——行选择模型。行选择模型默认支持多选,鼠标单击时按住ctrl/shift就可以选择多行。如果只希望选择一行,设置singleSelect参数为true。
在Grid中设置sm为RowSelectionModel,并指定singleSelect为true。
var grid1 = newExt.grid.GridPanel({
title : '员工信息',
renderTo : 'grid1',
cm : cm,
store : store,
width : 700,
height : 350,
enableColumnMove : false,
enableColumnResize : true,
loadMask : true,
viewConfig:{
forceFit:true,
getRowClass:function(record,rowIndex,p,ds){
if (rowIndex %2 == 0) {
return'oushu-row-color';
}
return'jishu-row-color';
}
},
autoExpandColumn : 'descn',
sm:new Ext.grid.RowSelectionModel({
singleSelect:true
})
});