今天项目遇到一个问题,需要使用extjs4 的grid的动态列,即列表的列需要根据需要动态的生成。
思路:定义grid时,columns根据配置获取,store中model的fields也根据配置动态获取。但是发现store中model的fields是很难改变的,因为很难拿到model对象,来执行model.setFields()方法。经过查看AbstractStore的源码发现,extjs4为了兼容extjs3当store中不给配置model属性,而配置了fields属性的时候,store会默认生成一个ImplicitModel。所以要实现动态的列,只需给store配置动态proxy(对应不同的url)、和动态的fields即可。
(只需关注标红的代码和添加了注释的代码即可,其它的代码是方便我日后参考)
AbstractStore生成自定义model的代码如下:
//Supports the 3.x style of simply passing an array of fields to the store, implicitly creating a model if (!me.model && me.fields) { me.model = Ext.define('Ext.data.Store.ImplicitModel-' + (me.storeId || Ext.id()), { extend: 'Ext.data.Model', fields: me.fields, proxy: me.proxy || me.defaultProxyType }); delete me.fields; me.implicitModel = true; }
定义的store代码如下:
Ext.define('shinow.ux.drugSearch.DrugSearchComboStore', { extend : 'Ext.data.Store', constructor : function(cfg) { var me = this; cfg = cfg || {}; me.callParent([Ext.apply({}, cfg)]); } });
定义的combobox的代码如下:
Ext.require('shinow.shinow.ux.field.GridComboBox'); Ext.define('shinow.ux.drugSearch.DrugSearchCombo', { extend : 'shinow.ux.field.GridComboBox' , requires : ['shinow.shinow.ux.drugSearch.DrugSearchComboStore'], alias : 'widget.drugSearchCombo', constructor : function(cfg) { var me = this; cfg = cfg || {}; me.callParent([ Ext.apply({ contentWidth : 500, contentHeight : 400, pageSize : 15, queryMode : 'remote', minChars : 2 },cfg) ]); } , initComponent : function() { var me = this; var proxy = { type : 'ajax', url : me.url,//动态配置url actionMethods : { create : "POST", read : "POST", update : "POST", destroy : "POST" }, reader : { type : 'json', root : 'resultData.data', totalProperty : 'resultData.totalProperty' } }; if(me.url && me.fields) { //此处将动态的配置信息传入store中,实现动态列。proxy提供数据源,fields实现表格的列 me.store = Ext.create('shinow.ux.drugSearch.DrugSearchComboStore',{proxy : proxy ,fields : me.fields}); }else { Ext.Msg.alert('警告','请配置控件的url和fields属性!'); } me.callParent(arguments); } });
下拉grid GridComboBox 代码如下:
Ext.define("shinow.ux.field.GridComboBox", { extend: "Ext.form.field.ComboBox", alias: ["widget.gridcombobox", "widget.gcombobox", "widget.gcombo"], requires: ["shinow.shinow.ux.grid.GridList", "shinow.shinow.ux.grid.GridListKeyNav"], contentWidth: 400, contentHeight: 400, queryCaching: false, matchFieldWidth: false, initComponent: function() { var a = this; if (!a.queryMode || a.queryMode == "remote") { if (!a.triggerAction || a.triggerAction == "all") { a.triggerAction = "query" } } a.callParent() }, onmouseupEvn: function(c, b, a) { c.stopEvent(); if (this.editable) { this.inputEl.focus() } }, createPicker: function() { var c = this; var d = [{ xtype: "rownumberer", resizable: true }]; if (c.columns) { d = d.concat(c.columns) } if (!c.listConfig) { c.listConfig = {} } c.listConfig.columns = d; var b, a = Ext.apply({ xtype: "gridlist", pickerField: c, selModel: { mode: c.multiSelect ? "SIMPLE" : "SINGLE" }, border: false, height: c.contentHeight, width: c.contentWidth, cls: "x-boundlist", floating: true, hidden: true, store: c.store, displayField: c.displayField, focusOnToFront: false, pageSize: c.pageSize }, c.listConfig, c.defaultListConfig); b = c.picker = Ext.widget(a); if (c.pageSize) { b.pagingToolbar.on("beforechange", c.onPageChange, c) } c.mon(b, { itemclick: c.onItemClick, refresh: c.onListRefresh, scope: c }); c.mon(b.getSelectionModel(), { beforeselect: c.onBeforeSelect, beforedeselect: c.onBeforeDeselect, selectionchange: c.onListSelectionChange, scope: c }); var h = c.valueModels || [], g = h.length, i, f; var e = []; for (i = 0; i < g; i++) { f = h[i]; if (f && f.isModel && c.store.indexOf(f) >= 0) { e.push(f) } } if (e.length) { b.show(); b.getView().getSelectionModel().select(e, undefined, true) } c.mon(c.inputEl, "mousedown", c.onmouseupEvn, c); return b }, getSelMode: function() { return this.valueModels }, onExpand: function() { var d = this, a = d.listKeyNav, c = d.selectOnTab, b = d.getPicker(); if (a) { a.enable() } else { a = d.listKeyNav = new shinow.ux.grid.GridListKeyNav(this.inputEl, { gridList: b, forceKeyDown: true, tab: function(f) { if (c) { this.selectHighlighted(f); d.triggerBlur() } return true }, enter: function(h) { var f = b.getSelectionModel(), g = f.getCount(); this.selectHighlighted(h); if (!d.multiSelect && g === f.getCount()) { d.collapse() } } }) } if (c) { d.ignoreMonitorTab = true } Ext.defer(a.enable, 1, a); d.inputEl.focus() }, doAutoSelect: function() { var b = this, a = b.picker, c, d; if (a && b.autoSelect && b.store.getCount() > 0) { c = a.getSelectionModel().lastSelected; d = a.getView().getNode(c || 0); if (d) { a.getView().highlightItem(d); a.getView().getTargetEl().scrollChildIntoView(d, false) } } }, alignPicker: function() { var b = this, a = b.getPicker(); if (b.isExpanded) { if (b.matchFieldWidth) { a.setWidth(b.bodyEl.getWidth()) } if (a.isFloating()) { b.doAlign() } } }, onTriggerClick: function() { var a = this; if (!a.readOnly && !a.disabled) { if (a.isExpanded) { a.collapse() } else { a.onFocus({}); if (a.triggerAction === "all") { a.doQuery(a.allQuery, true) } else { if (a.triggerAction === "last") { a.doQuery(a.lastQuery, true) } else { a.doQuery(a.getRawValue(), true, true) } } } a.inputEl.focus() } }, doRemoteQuery: function(b) { var c = this, a = function() { c.afterQuery(b) }; if (c.pageSize) { c.loadPage(1, { rawQuery: b.rawQuery, callback: a }) } else { c.store.load({ params: c.getParams(b.query), rawQuery: b.rawQuery, callback: a }) } c.expand() } });
GridList代码如下:
Ext.define("shinow.ux.grid.GridList", { extend: "Ext.panel.Table", requires: ["Ext.grid.View"], alias: ["widget.gridlist"], alternateClassName: ["Ext.list.ListView", "Ext.ListView", "Ext.grid.GridPanel"], viewType: "gridview", lockable: false, rowLines: true, deferRowRender: false, columnLines: true, initComponent: function() { var a = this; a.callParent(); if (a.pageSize) { a.pagingToolbar = a.createPagingToolbar(); a.addDocked(a.pagingToolbar) } }, createPagingToolbar: function() { return Ext.widget("pagingtoolbar", { id: this.id + "-paging-toolbar", pageSize: this.pageSize, store: this.store, dock: "bottom", displayInfo: true, border: false, ownerCt: this, ownerLayout: this.getComponentLayout() }) } });
GridListKeyNav代码如下:
Ext.define("shinow.ux.grid.GridListKeyNav", { extend: "Ext.util.KeyNav", requires: "shinow.shinow.ux.grid.GridList", constructor: function(b, a) { var c = this; c.gridList = a.gridList; c.callParent([b, Ext.apply({}, a, c.defaultHandlers)]) }, defaultHandlers: { up: function() { var e = this, b = e.gridList, d = b.getView().all, f = b.getView().highlightedItem, c = f ? b.getView().indexOf(f) : -1, a = c > 0 ? c - 1 : d.getCount() - 1; e.highlightAt(a) }, down: function() { var e = this, b = e.gridList, d = b.getView().all, f = b.getView().highlightedItem, c = f ? b.getView().indexOf(f) : -1, a = c < d.getCount() - 1 ? c + 1 : 0; e.highlightAt(a) }, pageup: function() {}, pagedown: function() {}, home: function() { this.highlightAt(0) }, end: function() { var a = this; a.highlightAt(a.gridList.all.getCount() - 1) }, enter: function(a) { this.selectHighlighted(a) } }, highlightAt: function(a) { var b = this.gridList, c = b.getView().all.item(a); if (c) { c = c.dom; b.getView().highlightItem(c); b.getView().getTargetEl().scrollChildIntoView(c, false) } }, selectHighlighted: function(f) { var d = this, b = d.gridList, c = b.getView().highlightedItem, a = b.getSelectionModel(); if (c) { a.selectWithEvent(b.getView().getRecord(c), f) } } });
引用下拉列表的代码如下:\\这段代码就是如何使用自己定义的下拉列表类
{ xtype : 'drugSearchCombo', fieldLabel :'药品查询', labelAlign : 'right', contentWidth : 500, contentHeight : 400, width : 200, pageSize : 15, queryMode : 'remote', minChars : 2, url : contentPath + '/chargeItem/queryChargeItemList.action',//动态配置url数据源地址 fields : [//动态配置fields决定列表的列 { //收费项目ID name : 'feeInfoID' } ,{ //收费项目类型ID name : 'feeTypeID' } ,{ //收费项目分类(主键ID) name : 'feeKindID' } ,{ //收费项目编码 name : 'feeInfoCode' } ,{ //收费项目名称 name : 'feeInfoName' } ], columns : [//动态配置列表的列 { text : '收费项目ID', dataIndex : 'feeInfoID', flex : 1 } ,{ text : '收费项目类型ID', dataIndex : 'feeTypeID', flex : 1 } ,{ text : '收费项目分类', dataIndex : 'feeKindID', flex : 1 } ,{ text : '收费项目编码', dataIndex : 'feeInfoCode', flex : 1 } ,{ text : '收费项目名称', dataIndex : 'feeInfoName', flex : 1 } ] }
注意:使用需要在view中,通过requires方式,将控件引入
requires : ['shinow.shinow.ux.drugSearch.DrugSearchCombo']