最近為了實現一個屬性下拉框被Ext框架折騰了好幾天。。 所以,首先要說的是,不管你要做什麼系統、強烈建議你不要選擇Ext。據我這幾天的搜索,應該這個框架現在用的人也很少了。 Ext框架的缺陷:框架沉重、擴展性差(與其他js框架相比)、各版本差別大(Ext3、4、5不相容)。 現在進入正題,這幾天研究 ...
最近為了實現一個屬性下拉框被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 });