当前位置: 首页
前端开发
EXTJS记事本中复合字段与行编辑器的结合实践

EXTJS记事本中复合字段与行编辑器的结合实践

热心网友 时间:2026-06-15
转载

在RowEditor中利用CompositeField嵌入三个ComboBox实现物料级联筛选,需手动处理控件初始化与Change事件。通过扩展afterstart事件进行控件赋值,在validateedit中校验重复物料,并在afteredit中手动将值写回记录。关键是通过editor items items[0]访问CompositeField子组件。

EXTJS记事本 当CompositeField遇上RowEditor 在进行BOM(物料清单)编辑时,经常需要在RowEditor中实现级联下拉菜单。这个需求看似简单,实际开发中却有不少陷阱。 背景是这样的:客户的物料种类繁多,多达一千多种。如果仅使用单个ComboBox,用户查找物料几乎只能靠猜测。因此,我设计了通过“物料分类”和“物料品牌”两个ComboBox进行级联筛选的方案。然而,问题也随之而来:在RowEditor的一个字段内嵌入多个控件,意味着需要手动管理每个控件的初始化及Change事件。我搜索了网上现有方案,发现没有合适的现成解决方案。经过整整三天的调试,终于成功解决了这一难题。 下面展示RowEditor的定义及关键事件处理代码。

复制代码 代码如下:

var editor=new Ext.ux.grid.RowEditor({ sa veText: '确定', cancelText:"放弃", commitChangesText: '请确定或放弃修改', errorText: '错误' }); // 当取消时,根据关键字段值是否为空来删除空记录 editor.on("canceledit",function(editor,pressed) { if(pressed && editor.record.get("materialid")==0) { store.remove(editor.record); } },this); /* * afterstart 这个事件是自己加的。 * 在beforeedit事件里想初始化自定义控件?不行,因为那时候RowEditor还没渲染。 * 所以自己扩展了一个afterstart事件——它在RowEditor显示后立即触发,这里做初始化再合适不过。 * 还有一处关键:通过RowEditor控件遍历来访问自定义的CompositeField。 * 注意写法:editor.items.items[0] * 这并不是写重了,而是RowEditor的items竟然不是一个数组,而是一个对象。这部分也耗费了不少时间,最后是靠FireBug打印出editor对象才发现的。 * editor.items.items[0] 就是CompositeField组件,通过它的items集合可以标准访问子组件,剩下的初始化就好办了。 * 最后一个ComboBox的数据依赖前两个ComboBox级联选取,所以需要在这里动态加载数据。 * 重点:必须在callback中执行,因为JsonStore的load是异步的,数据加载成功后,再用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 在通过校验后执行。 * 这一步至关重要:因为使用了CompositeField,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作为插件集成到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} }

下面是最关键的列定义部分,展示了CompositeField的具体配置方式。

复制代码 代码如下:

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。 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属性来判断是否触发validateedit和afteredit。 // 它实际上检测的是每列对应控件的值是否变化。由于物料型号这一列对应的是CompositeField, // 所以必须强制让CompositeField的值变化,RowEditor才会正常调用validateedit和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 } ] ]

以上经验分享给大家,希望对遇到类似问题的朋友有所帮助。
来源:https://www.jb51.net/article/27844.htm

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
如何用拖放在Flask看板应用中更新数据库状态

如何用拖放在Flask看板应用中更新数据库状态

在Flask看板应用中,通过HTML5拖放API结合前端fetch异步POST请求,将拖拽任务的task_id和target_state发送至后端路由;后端接收JSON后更新数据库并提交事务,实现跨列拖拽时实时更新状态。需注意将事件绑定到容器而非子元素,并在ondragover中阻止默认行为以确保drop触发。

时间:2026-07-09 07:01
如何用CSS :has()选择器快速实现悬停span显示相邻div

如何用CSS :has()选择器快速实现悬停span显示相邻div

利用CSS的:has()伪类选择器,通过` bar:has(> baz:hover)+ qux`实现悬停内联元素时显示相邻块级容器,解决了传统相邻兄弟选择器无法跨层级匹配的局限,可用于悬停图标显示详情框等交互场景。需注意浏览器兼容性和性能优化,避免过度使用影响渲染效率。

时间:2026-07-09 07:01
display:block居中段落文本的响应式实现方法

display:block居中段落文本的响应式实现方法

通过`display:block`与`max-width`限制宽度,配合`margin:auto`使段落块水平居中并实现响应式布局;同时用CSS统一控制段落间距,避免脱离文档流,提升可读性与维护性。

时间:2026-07-09 07:01
JavaScript代码去重:高效避免重复编写的技巧

JavaScript代码去重:高效避免重复编写的技巧

将重复代码中的变化点提取为参数,利用ES6模板字符串动态生成选择器,可消除重复、保持一致性。注意参数安全性,必要时进行白名单校验;批量化场景可扩展为数组形式;现代框架中更推荐数据驱动或组件化封装替代手动选择器。

时间:2026-07-09 07:00
使用容器查询单位cqw实现表单输入框相对于祖父容器百分比宽度

使用容器查询单位cqw实现表单输入框相对于祖父容器百分比宽度

利用CSS容器查询单位cqw,表单输入框宽度可直接基于祖父容器计算,绕过中间包装器尺寸干扰,实现简洁响应式布局,无需硬编码或JavaScript,显著降低维护成本,提升嵌套结构下的布局灵活性。

时间:2026-07-09 07:00
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜