最近为了实现一个属性下拉框被Ext框架折腾了好几天。。
所以,首先要说的是,不管你要做什么系统、强烈建议你不要选择Ext。据我这几天的搜索,应该这个框架现在用的人也很少了。
Ext框架的缺陷:框架沉重、扩展性差(与其他js框架相比)、各版本差别大(Ext3、4、5不兼容)。
现在进入正题,这几天研究ext实现树形下拉框发现网上常见的两种做法:
1、扩展Ext.form.field.ComboBox
使用这种方式的好处是ComboBox与我们要实现的东西其行为更为相似,在取值、赋值方面比较方便、可以对触发下拉事件上做定制。
最后实现的东西发现浏览器不能兼容、只有Win10的Edge可以完美呈现,无奈放弃。。
2、扩展Ext.form.field.Picker
上一条路没有走通最终选择了这种方式。这种方式的好处:对树形节点的展开、关闭不会影响到用户的选取操作。
这段代码也是根据网络上的源码做了修改,之所以发出来因为网上实在是找不到基于Ext5的实现。所以我判断现在使用Ext框架的人应该极少了,网上有的都是几年前使用Ext4、3的人发表的。所有基于Ext4的实现在Ext5上都不能完美支持。。
定义组件:
Ext.define('Ext.ux.ComboBoxTree', { extend: 'Ext.form.field.Picker', requires: ['Ext.tree.Panel'], alias: ['widget.comboboxtree'], multiSelect: false, multiCascade: true, rootVisible: false, displayField: 'text', emptyText:'', submitValue: '', url:'', pathValue: '', defaultValue: null, pathArray: [], selectNodeModel: 'all', setValue: function (value) { if (value) {//注意:此处的判断会使id为0的值选中失效 if (typeof value == 'number') { this.defaultValue = value; } this.callParent(arguments); } }, initComponent: function () { var self = this; self.selectNodeModel = Ext.isEmpty(self.selectNodeModel) ? 'all' : self.selectNodeModel; Ext.apply(self, { fieldLabel: self.fieldLabel, labelWidth: self.labelWidth }); self.store = Ext.create('Ext.data.TreeStore', { root: { expanded: true }, proxy: { type: 'ajax', url: self.url }, autoLoad: true }); self.store.addListener('load', function (st, rds, opts) { if (self.defaultValue) { var defaultRecord = self.store.getNodeById(self.defaultValue); self.setDefaultValue(defaultRecord.get('id'), defaultRecord.get('text')); } else { self.setDefaultValue('', self.emptyText); } }); self.callParent(); }, createPicker: function () { var self = this; self.picker = Ext.create('Ext.tree.Panel', { //height: self.treeHeight == null ? 200 : self.treeHeight, autoScroll: true, floating: true, focusOnToFront: false, shadow: true, ownerCt: this.ownerCt, useArrows: false, store: this.store, rootVisible: this.rootVisible, displayField: this.displayField, viewConfig: { onCheckboxChange: function (e, t) { if (self.multiSelect) { var item = e.getTarget(this.getItemSelector(), this.getTargetEl()), record; if (item) { record = this.getRecord(item); var check = !record.get('checked'); record.set('checked', check); if (self.multiCascade) { if (check) { record.bubble(function (parentNode) { if ('Root' != parentNode.get('text')) { parentNode.set('checked', true); } }); record.cascadeBy(function (node) { node.set('checked', true); node.expand(true); }); } else { record.cascadeBy(function (node) { node.set('checked', false); }); record.bubble(function (parentNode) { if ('Root' != parentNode.get('text')) { var flag = true; for (var i = 0; i < parentNode.childNodes.length; i++) { var child = parentNode.childNodes[i]; if (child.get('checked')) { flag = false; continue; } } if (flag) { parentNode.set('checked', false); } } }); } } } var records = self.picker.getView().getChecked(), names = [], values = []; Ext.Array.each(records, function (rec) { names.push(rec.get('text')); values.push(rec.get('id')); }); self.submitValue = values.join(','); self.setValue(names.join(',')); } } } }); self.picker.on({ itemclick: function (view, recore, item, index, e, object) { var selModel = self.selectNodeModel; var isLeaf = recore.data.leaf; var isRoot = recore.data.root; var view = self.picker.getView(); if (!self.multiSelect) { if ((isRoot) && selModel != 'all') { return; } else if (selModel == 'exceptRoot' && isRoot) { return; } else if (selModel == 'folder' && isLeaf) { return; } else if (selModel == 'leaf' && !isLeaf) { var expand = recore.get('expanded'); if (expand) { view.collapse(recore); } else { view.expand(recore); } return; } self.submitValue = recore.get('id'); self.setValue(recore.get('text')); self.eleJson = Ext.encode(recore.raw); self.collapse(); } } }); return self.picker; }, listeners: { expand: function (field, eOpts) { var picker = this.getPicker(); if (!this.multiSelect) { if (this.pathValue != '') { picker.expandPath(this.pathValue, 'id', '/', function (bSucess, oLastNode) { picker.getSelectionModel().select(oLastNode); }); } } else { if (this.pathArray.length > 0) { for (var m = 0; m < this.pathArray.length; m++) { picker.expandPath(this.pathArray[m], 'id', '/', function (bSucess, oLastNode) { oLastNode.set('checked', true); }); } } } } }, clearValue: function () { this.setDefaultValue('', ''); }, getEleJson: function () { if (this.eleJson == undefined) { this.eleJson = []; } return this.eleJson; }, getSubmitValue: function () { if (this.submitValue == undefined) { this.submitValue = ''; } return this.submitValue; }, getDisplayValue: function () { if (this.value == undefined) { this.value = ''; } return this.value; }, getValue: function () { return this.getSubmitValue(); }, setPathValue: function (pathValue) { this.pathValue = pathValue; }, setPathArray: function (pathArray) { this.pathArray = pathArray; }, setDefaultValue: function (submitValue, displayValue) { this.submitValue = submitValue; this.setValue(displayValue); this.eleJson = undefined; this.pathArray = []; }, alignPicker: function () { var me = this, picker, isAbove, aboveSfx = '-above'; if (this.isExpanded) { picker = me.getPicker(); if (me.matchFieldWidth) { picker.setWidth(me.bodyEl.getWidth()); } if (picker.isFloating()) { picker.alignTo(me.inputEl, "", me.pickerOffset); // ""->tl isAbove = picker.el.getY() < me.inputEl.getY(); me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx); picker.el[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx); } } }});
以上代码有待优化,由于只用了单选,所以多选应该还有点问题。
调用代码:
Ext.create('Ext.ux.ComboBoxTree', { cId: 'cbOrganizationId', name: 'OrganizationId', fieldLabel: '所属组织', editable: false, url: urls.SaleInfo.GetOrganizationTree, //emptyText: '请选择所属组织', allowBlank: false });
Ext.form.field.ComboBoxView source...
原标题:Ext5实现树形下拉框ComboxTree
关键词: