用RowEditor作批量编辑器时,遇到一个问题,想要在Roweditor中使用三个下拉列表组成级联式选择控件,原因是客户的物料种类非常多,有一千种之多,如果单纯用一个Combobox,那么在实际使用中,很难快速找到一个物料,所以,我使用包含物料分类和物料品牌的两个combobox来组成级联式筛选。问题恰恰出在这儿,如果在roweditor的一个字段中用多个控件,就要处理每个控件的初始化,Change事件。网上目前还未找到有人有好的解决办法。经过3天的调试,我终于解决了问题,把我的代码贴出来:
var editor=new Ext.ux.grid.RowEditor({
saveText:
'
确定
'
,
cancelText: " 放弃 " ,
commitChangesText: ' 请确定或放弃修改 ' ,
errorText: ' 错误 '
});
// 当取消时,根据关键字段的值是否为空而删掉空记录
editor.on( " canceledit " , function (editor,pressed)
{
if (pressed && editor.record.get( " materialid " ) == 0 )
{
store.remove(editor.record);
}
}, this );
/*
afterstart 这个事件是自己加的,因为如果在beforeedit事件中想对自己的控件初始化,那是不可能的,因为beforeedit时,roweditor控件还没有渲染,所以,我加了afterstart事件,该事件在roweditor显示后立即调用,所以,可以在这里进行初始化。
要注意的是通过roweditor控件进行遍历来访问自定义的composite控件
editor.items.items[0],这里并不是我写重了,而是roweditor控件的items竟然不是一个集合,而是一个对象,在这里我也耗了一些时间,最后还是通过firebug输出editor对象发现的
editor.items.items[0]就是compositefield组件,通过该组件的items集合,就可以以标准的形式访问其子组件,接下来,就可以初始化了
因为最后一个combobox的数据是要通过前两个combobox级联选取后载入的,所以,在这里载入其数据进行初始化,但是注意,我是在callback中执行的,因为jsonstore的load动作是异步的,所以,必须通过callback事件的回调在数据载入成功后,再用setValue来初始化值
*/
editor.on( " afterstart " , function (editor,rowIndex)
{
var record = store.getAt(rowIndex);
editor.items.items[ 0 ].items.items[ 0 ].setValue(record.get( " setid " ));
editor.items.items[ 0 ].items.items[ 1 ].setValue(record.get( " category " ));
var t_store = editor.items.items[ 0 ].items.items[ 2 ].getStore();
t_store.load({
params:{category:record.get( " category " ),setid:record.get( " setid " )},
callback: function (r,options,success){
if (success)
editor.items.items[ 0 ].items.items[ 2 ].setValue(record.get( " materialid " ));
}
});
}, this );
/*
validateedit事件是在按了确认时执行的,用来验证roweditor中各控件的值,在这里,我执行了一个自定义的验证动作,因为我不想用户可以添加重复的物料,所以,我通过遍历jsonstore,将每条记录的物料值与用户选择的物料值进行比较,如果发现已经存在,则提示用户不要重复加入
*/
editor.on( " validateedit " , function (editor,obj,record,rowIndex){
var materialid = editor.items.items[ 0 ].items.items[ 2 ].getValue();
var exist = false ;
Ext.each(store.getRange(), function (o,i){
if (o != record && o.get( " materialid " ) == materialid)
{
exist = true ;
return ( false );
}
});
if (exist)
{
Ext.MessageBox.alert( " 系统提示 " , " 请勿重复添加 " );
store.remove(record);
}
return ( ! exist);
}, this );
/*
afterEdit是通过验证后执行的,这里最重要的动作是将正在编辑的记录的某些属性赋值,原因是由于采用了compsitefield,所以,roweditor无法将选取的值赋给record的正确属性,需要我们手工将用户的选择赋给相应的字段,materialid就是用户选的物料编号,而model对应是该物料的型号
为什么要赋model呢?因为model是列的值嘛,不赋的话,显示的是空的
*/
editor.on( " afteredit " , function (editor,obj,record,rowIndex){
record.set( " materialid " ,editor.items.items[ 0 ].items.items[ 2 ].getValue());
record.set( " model " ,editor.items.items[ 0 ].items.items[ 2 ].getRawValue());
}, this );
cancelText: " 放弃 " ,
commitChangesText: ' 请确定或放弃修改 ' ,
errorText: ' 错误 '
});
// 当取消时,根据关键字段的值是否为空而删掉空记录
editor.on( " canceledit " , function (editor,pressed)
{
if (pressed && editor.record.get( " materialid " ) == 0 )
{
store.remove(editor.record);
}
}, this );
/*
afterstart 这个事件是自己加的,因为如果在beforeedit事件中想对自己的控件初始化,那是不可能的,因为beforeedit时,roweditor控件还没有渲染,所以,我加了afterstart事件,该事件在roweditor显示后立即调用,所以,可以在这里进行初始化。
要注意的是通过roweditor控件进行遍历来访问自定义的composite控件
editor.items.items[0],这里并不是我写重了,而是roweditor控件的items竟然不是一个集合,而是一个对象,在这里我也耗了一些时间,最后还是通过firebug输出editor对象发现的
editor.items.items[0]就是compositefield组件,通过该组件的items集合,就可以以标准的形式访问其子组件,接下来,就可以初始化了
因为最后一个combobox的数据是要通过前两个combobox级联选取后载入的,所以,在这里载入其数据进行初始化,但是注意,我是在callback中执行的,因为jsonstore的load动作是异步的,所以,必须通过callback事件的回调在数据载入成功后,再用setValue来初始化值
*/
editor.on( " afterstart " , function (editor,rowIndex)
{
var record = store.getAt(rowIndex);
editor.items.items[ 0 ].items.items[ 0 ].setValue(record.get( " setid " ));
editor.items.items[ 0 ].items.items[ 1 ].setValue(record.get( " category " ));
var t_store = editor.items.items[ 0 ].items.items[ 2 ].getStore();
t_store.load({
params:{category:record.get( " category " ),setid:record.get( " setid " )},
callback: function (r,options,success){
if (success)
editor.items.items[ 0 ].items.items[ 2 ].setValue(record.get( " materialid " ));
}
});
}, this );
/*
validateedit事件是在按了确认时执行的,用来验证roweditor中各控件的值,在这里,我执行了一个自定义的验证动作,因为我不想用户可以添加重复的物料,所以,我通过遍历jsonstore,将每条记录的物料值与用户选择的物料值进行比较,如果发现已经存在,则提示用户不要重复加入
*/
editor.on( " validateedit " , function (editor,obj,record,rowIndex){
var materialid = editor.items.items[ 0 ].items.items[ 2 ].getValue();
var exist = false ;
Ext.each(store.getRange(), function (o,i){
if (o != record && o.get( " materialid " ) == materialid)
{
exist = true ;
return ( false );
}
});
if (exist)
{
Ext.MessageBox.alert( " 系统提示 " , " 请勿重复添加 " );
store.remove(record);
}
return ( ! exist);
}, this );
/*
afterEdit是通过验证后执行的,这里最重要的动作是将正在编辑的记录的某些属性赋值,原因是由于采用了compsitefield,所以,roweditor无法将选取的值赋给record的正确属性,需要我们手工将用户的选择赋给相应的字段,materialid就是用户选的物料编号,而model对应是该物料的型号
为什么要赋model呢?因为model是列的值嘛,不赋的话,显示的是空的
*/
editor.on( " afteredit " , function (editor,obj,record,rowIndex){
record.set( " materialid " ,editor.items.items[ 0 ].items.items[ 2 ].getValue());
record.set( " model " ,editor.items.items[ 0 ].items.items[ 2 ].getRawValue());
}, this );
以上是roweditor的定义和对事件的处理,接下来,将roweditor作为插件插入到gridpanel
{
xtype:
"
grid
"
,
title: " 产品BOM " ,
layout: " fit " ,
store:store,
enableDragDrop: false ,
border: false ,
frame: false ,
autoScroll: true ,plugins:[editor],
sm:sm,
height: 340 ,
clicksToEdit: 2 ,
autoWidth: true ,
viewConfig:{forceFit: true ,autoFill: true ,markDirty: false }
}
title: " 产品BOM " ,
layout: " fit " ,
store:store,
enableDragDrop: false ,
border: false ,
frame: false ,
autoScroll: true ,plugins:[editor],
sm:sm,
height: 340 ,
clicksToEdit: 2 ,
autoWidth: true ,
viewConfig:{forceFit: true ,autoFill: true ,markDirty: false }
}
接下来,再看看关于gridpanel的列定义,这里,你可以看到composite是如何用的
columns: [{
header:
"
物料名称/型号
"
,
dataIndex: " model " ,
width: 200 ,
menuDisabled: true ,
editor:
{
// 定义编辑器
xtype: " compositefield " ,
name: " compositefield " ,
items:[
{
xtype: " combo " ,
mode: " local " ,
name: " sets " ,
width: 80 ,
fieldLabel: " 适用产品品牌 " ,
emptyText: " 请选择 " ,
valueField: " id " ,
lazyInit: false ,
value: this .data ? this .data.title: "" ,
hiddenName: " setid " ,
hiddenValue: this .data ? this .data.setid: "" ,
displayField: " title " ,
typeAhead: false ,
forceSelection: true ,
editable: true ,
listeners:{
" change " : function (combo,newvalue,oldvalue)
{
// 处理品牌的change事件,在选取品牌后,重新载入combobox,editor就是前文定义的roweditor的实例
var category = editor.items.items[ 0 ].items.items[ 1 ];
var material = editor.items.items[ 0 ].items.items[ 2 ];
var c = category.getValue();
var store = material.getStore();
store.load({
params:{setid:newvalue,category:c},
callback: function (r,options,success){
if (success)
material.setValue( "" );
}
});
}
},
triggerAction: " all " ,
store: new Ext.data.JsonStore({
url: " <%=script_path%>data.asp " ,
root: " data " ,autoDestroy: true ,
remoteSort: true ,
listeners:{ " load " : function (store,records,option){
var s = Ext.data.Record.create([{name: " id " ,type: " int " },{name: " title " ,type: " string " }]);
store.add( new s({id: 0 ,title: " 通用 " }))
}},
baseParams: {op: " setList " },
totalProperty: " total " ,
autoLoad: true ,
fields: [ " title " , " id " ]
})
},
{
xtype: " combo " ,
mode: " local " ,width: 60 ,
name: " category " ,
fieldLabel: " 类别 " ,
emptyText: " 请选择 " ,
valueField: " category " ,
lazyInit: false ,
value: this .data ? this .data.category: "" ,
displayField: " category " ,
typeAhead: false ,forceSelection: true ,
triggerAction: " all " ,
listeners:{
" change " : function (combo,newvalue,oldvalue)
{
// 处理类别的change事件,在选取品牌后,重新载入combobox,editor就是前文定义的roweditor的实例
var sets = editor.items.items[ 0 ].items.items[ 0 ];
var material = editor.items.items[ 0 ].items.items[ 2 ];
var setid = sets.getValue();
var store = material.getStore();
store.load({
params:{category:newvalue,setid:setid},
callback: function (r,options,success){
if (success)
material.setValue( "" );
}
});
}
},
store: new Ext.data.JsonStore({
url: " <%=script_path%>data.asp " ,
root: " data " ,autoDestroy: true ,
remoteSort: true ,
baseParams: {op: " materialCategoryList " },
totalProperty: " total " ,
autoLoad: true ,
fields: [ " category " ]
})
},
{
xtype: " combo " ,
forceSelection: true ,
editable: true ,
mode: " local " ,
name: " material " ,
fieldLabel: " 物料 " ,
emptyText: " 请选择物料 " ,
valueField: " id " ,
allowBlank: false ,
displayField: " model " ,
width: 250 ,
lazyInit: false ,
typeAhead: false ,
triggerAction: " all " ,
listeners:{
" change " : function (combo,newvalue,oldvalue)
{
// 这里一定要注意!!!如果没有下面这两句,那你选择后,会发现显示的值不会变化,并且,点了确认,也不能更新。为什么呢?因为roweditor是通过检测record的isdirty属性来决定是不是调用validateedito和afteredit的,它是检测每列对应的控件值是否变化来判断的,由于物料型号这列,对应的是compositefield,所以,我们必须让compositefield值发生变化,roweditor才会调用validedit和afteredit,并且,compositefield的值还会被调用来显示在列里
var comp = editor.items.items[ 0 ];
comp.setRawValue(combo.getRawValue());
}
},
store: new Ext.data.JsonStore({
url: " <%=script_path%>data.asp " ,
root: " data " ,autoDestroy: true ,
remoteSort: true ,
baseParams: {op: " materialList " },
totalProperty: " total " ,
autoLoad: false ,
fields: [ " model " , " id " ]
})}
]
}
},
{
header: " 数量 " ,
dataIndex: " qty " ,
width: 50 ,
menuDisabled: true ,
editor: {
xtype: ' numberfield ' ,
minValue: 1 ,
allowDecimals: false
}
}
,{
header: " 颜色 " ,
dataIndex: " color " ,
width: 60 ,
menuDisabled: true
}
,{
header: " 尺寸 " ,
dataIndex: " size " ,
width: 60 ,
menuDisabled: true
}
]
}
]
dataIndex: " model " ,
width: 200 ,
menuDisabled: true ,
editor:
{
// 定义编辑器
xtype: " compositefield " ,
name: " compositefield " ,
items:[
{
xtype: " combo " ,
mode: " local " ,
name: " sets " ,
width: 80 ,
fieldLabel: " 适用产品品牌 " ,
emptyText: " 请选择 " ,
valueField: " id " ,
lazyInit: false ,
value: this .data ? this .data.title: "" ,
hiddenName: " setid " ,
hiddenValue: this .data ? this .data.setid: "" ,
displayField: " title " ,
typeAhead: false ,
forceSelection: true ,
editable: true ,
listeners:{
" change " : function (combo,newvalue,oldvalue)
{
// 处理品牌的change事件,在选取品牌后,重新载入combobox,editor就是前文定义的roweditor的实例
var category = editor.items.items[ 0 ].items.items[ 1 ];
var material = editor.items.items[ 0 ].items.items[ 2 ];
var c = category.getValue();
var store = material.getStore();
store.load({
params:{setid:newvalue,category:c},
callback: function (r,options,success){
if (success)
material.setValue( "" );
}
});
}
},
triggerAction: " all " ,
store: new Ext.data.JsonStore({
url: " <%=script_path%>data.asp " ,
root: " data " ,autoDestroy: true ,
remoteSort: true ,
listeners:{ " load " : function (store,records,option){
var s = Ext.data.Record.create([{name: " id " ,type: " int " },{name: " title " ,type: " string " }]);
store.add( new s({id: 0 ,title: " 通用 " }))
}},
baseParams: {op: " setList " },
totalProperty: " total " ,
autoLoad: true ,
fields: [ " title " , " id " ]
})
},
{
xtype: " combo " ,
mode: " local " ,width: 60 ,
name: " category " ,
fieldLabel: " 类别 " ,
emptyText: " 请选择 " ,
valueField: " category " ,
lazyInit: false ,
value: this .data ? this .data.category: "" ,
displayField: " category " ,
typeAhead: false ,forceSelection: true ,
triggerAction: " all " ,
listeners:{
" change " : function (combo,newvalue,oldvalue)
{
// 处理类别的change事件,在选取品牌后,重新载入combobox,editor就是前文定义的roweditor的实例
var sets = editor.items.items[ 0 ].items.items[ 0 ];
var material = editor.items.items[ 0 ].items.items[ 2 ];
var setid = sets.getValue();
var store = material.getStore();
store.load({
params:{category:newvalue,setid:setid},
callback: function (r,options,success){
if (success)
material.setValue( "" );
}
});
}
},
store: new Ext.data.JsonStore({
url: " <%=script_path%>data.asp " ,
root: " data " ,autoDestroy: true ,
remoteSort: true ,
baseParams: {op: " materialCategoryList " },
totalProperty: " total " ,
autoLoad: true ,
fields: [ " category " ]
})
},
{
xtype: " combo " ,
forceSelection: true ,
editable: true ,
mode: " local " ,
name: " material " ,
fieldLabel: " 物料 " ,
emptyText: " 请选择物料 " ,
valueField: " id " ,
allowBlank: false ,
displayField: " model " ,
width: 250 ,
lazyInit: false ,
typeAhead: false ,
triggerAction: " all " ,
listeners:{
" change " : function (combo,newvalue,oldvalue)
{
// 这里一定要注意!!!如果没有下面这两句,那你选择后,会发现显示的值不会变化,并且,点了确认,也不能更新。为什么呢?因为roweditor是通过检测record的isdirty属性来决定是不是调用validateedito和afteredit的,它是检测每列对应的控件值是否变化来判断的,由于物料型号这列,对应的是compositefield,所以,我们必须让compositefield值发生变化,roweditor才会调用validedit和afteredit,并且,compositefield的值还会被调用来显示在列里
var comp = editor.items.items[ 0 ];
comp.setRawValue(combo.getRawValue());
}
},
store: new Ext.data.JsonStore({
url: " <%=script_path%>data.asp " ,
root: " data " ,autoDestroy: true ,
remoteSort: true ,
baseParams: {op: " materialList " },
totalProperty: " total " ,
autoLoad: false ,
fields: [ " model " , " id " ]
})}
]
}
},
{
header: " 数量 " ,
dataIndex: " qty " ,
width: 50 ,
menuDisabled: true ,
editor: {
xtype: ' numberfield ' ,
minValue: 1 ,
allowDecimals: false
}
}
,{
header: " 颜色 " ,
dataIndex: " color " ,
width: 60 ,
menuDisabled: true
}
,{
header: " 尺寸 " ,
dataIndex: " size " ,
width: 60 ,
menuDisabled: true
}
]
}
]
谨以此记,分享给有需要的朋友