当前位置: 代码迷 >> Web前端 >> 分享上自己扩展的ComboBoxTree 上拉树组件
  详细解决方案

分享上自己扩展的ComboBoxTree 上拉树组件

热度:236   发布时间:2012-10-26 10:30:59.0
分享下自己扩展的ComboBoxTree 下拉树组件
代码贴出来如下,没什么好说的。具体看后面的如何应用。

Ext.ns('Ext.ux.form');
/**
 * customized field for ComboBox dropdown tree
 * 
 * @class Ext.ux.form.ComboBoxTree
 * @extends Ext.form.TwinTriggerField
 */
Ext.ux.form.ComboBoxTree = Ext.extend(Ext.form.TwinTriggerField,{

	//list dropdown Fx,boolean or object
	animate : {easing:'easeIn',duration:0.75},

	defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
	
	editable : false,
	
	//true to display the clear value trigger, default to false
	enableClearValue : false,
	
	//this hiddenField's init value if exist
	hiddenValue : '',
	
	//this field's display init value
	displayValue : '',
	
	//dropdown list's width,default to this field's width
	listWidth : undefined,
	
	//dropdown list's height,if tree's height was speciallized ignore this
	listHeight : undefined,
	
	maxListHeight : 225,
	
	minListWidth : 70,
	
	handleHeight : 8,
	
	listClass : '',
	
	selectedClass : 'x-combo-selected',
	
	shadow : 'sides',
	
	listAlign : 'tl-bl?',
	
	listEmptyText : '',
	
	resizable : false,
	
	trigger1Class : 'x-form-clear-trigger',
	
	hideTrigger1 : true,
	
    trigger2Class : 'x-form-arrow-trigger',
	
	initComponent : function(){
	
		this.addEvents(
			/**
             * @event expand
             * Fires when the dropdown list is expanded
             * @param {Ext.ux.form.ComboBoxTree}
             */
			'expand',
			
			/**
             * @event collapse
             * Fires when the dropdown list is collapsed
             * @param {Ext.ux.form.ComboBoxTree}
             */
			'collapse',
			
			/**
             * @event treenodeselect
             * Fires when the tree node is selected
             * @param {Ext.ux.form.ComboBoxTree}
             * @param {Ext.tree.TreeNode}
             * @param {Ext.EventObject}
             */
			'treenodeselect',
			
			/**
             * @event beforecollapse
             * Fires when the dropdown list before collapsed
             * @param {Ext.ux.form.ComboBoxTree}
             */
			'beforecollapse',
			
			/**
             * @event beforeexpand
             * Fires when the dropdown list before expanded
             * @param {Ext.ux.form.ComboBoxTree}
             */
			'beforeexpand',
			
			/**
             * @event clearvalue
             * Fires when the clear value trigger is clicked
             * @param {Ext.ux.form.ComboBoxTree}
             * @param {String} the value will be cleared
             */
			'clearvalue'
		);
		
		Ext.ux.form.ComboBoxTree.superclass.initComponent.call(this);
		
		//user both do not specialized treePanel'height and comboboxtree's listHeight,it's means dropdown list auto fit height
    	this.tree.height = Ext.isDefined(this.tree.height) ? this.tree.height : this.listHeight;
    	
        //disabled autoHeight
        this.tree.autoHeight = false;
        
        //always autoScroll = true
        this.tree.autoScroll = true;
        
        this.tree.containerScroll = true;
        
		this.tree.border = false;
		
		//if this.tree.height == undefined ,force to expand tree'root if user do not specialized expanded = true
		this.tree.root.expanded = !Ext.isDefined(this.tree.height) ? true : this.tree.root.expanded;
		
		this.tplId = Ext.id();
		
		this.tpl = '<div id="'+this.tplId+'"></div>';
		
		this.onTrigger2Click = this.onTriggerClick;
        
        this.onTrigger1Click = this.clearValue;
		
	},
	
	//private
	onRender : function(ct, position){
		
		if(this.hiddenName && !Ext.isDefined(this.submitValue)){
            this.submitValue = false;
        }
		
		Ext.ux.form.ComboBoxTree.superclass.onRender.call(this,ct,position);

		if(this.hiddenName){
			var formItem = this.el.findParent('.x-form-item',4,true);
			if(formItem){
				//create a Ext.form.Hidden field as a sibling of this, in order to Ext.form.BasicForm.load(..)
				this.hiddenField = formItem.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
						id: (this.hiddenId||this.hiddenName)}, 'after', true);	
				var hf = new Ext.form.Hidden({applyTo:this.hiddenField});
			
				var basicForm = this.getOwnerForm(this);
				if(basicForm){
					basicForm.add(hf);
				}
			}else{
				this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
                    id: (this.hiddenId||this.hiddenName)}, 'before', true);
			}
        }
	
        if(Ext.isGecko){
            this.el.dom.setAttribute('autocomplete', 'off');
        }
		
        this.initList();
	},
	
	//private
	initList : function(){
		if(!this.list){
			var cls = 'x-combo-list',
                listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
                zindex = parseInt(Ext.fly(listParent).getStyle('z-index'), 10);
	
            if (!zindex) {
                zindex = this.getParentZIndex();
            }

            this.list = new Ext.Layer({
                parentEl: listParent,
                shadow: this.shadow,
                cls: [cls, this.listClass].join(' '),
                constrain:false,
                zindex: (zindex || 12000) + 5
            });
			
			var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
            this.list.setSize(lw, 0);
			
			//specialized the tree's width for scroll -x
			this.tree.width = lw - 1;
			
            this.list.swallowEvent('mousewheel');
			this.assetHeight = 0;
			if(this.syncFont !== false){
                this.list.setStyle('font-size', this.el.getStyle('font-size'));
            }
            if(this.title){
                this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
                this.assetHeight += this.header.getHeight();
            }
			
			this.innerList = this.list.createChild({cls:cls+'-inner'});
			this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
			
			this.view = new Ext.DataView({
                applyTo: this.innerList,
                tpl: this.tpl,
                singleSelect: true,
                selectedClass: this.selectedClass,
                itemSelector: this.itemSelector || '.' + cls + '-item',
                emptyText: this.listEmptyText,
                deferEmptyText: false,
				listeners: {
					'afterrender' : function(){
						this.tpl.overwrite(this.el,[]);
					}
				}
            });
			
			this.mon(this.view, {
                containerclick : this.onViewClick,
                click : this.onViewClick,
                scope :this
            });
			
            if(this.resizable){
                this.resizer = new Ext.Resizable(this.list,  {
                   pinned:true, handles:'se'
                });
                this.mon(this.resizer, 'resize', function(r, w, h){
                    this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
                    this.listWidth = w;
                    this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
                    this.restrictHeight();
                }, this);
            }
		}
	},
	
	//private
	initEvents : function(){
	
		Ext.ux.form.ComboBoxTree.superclass.initEvents.call(this);
		
		this.tree.on({
			scope:this,
    		'render' : function(){
    			this.tree.getTreeEl().on('click',function(e,t,o){
    				if(e.getTarget('.x-tree-ec-icon', 1)){//is click tree's collapse/expand trigger icon area?
    					this.isTreeNodeClicked = false;
    				}else{
    					this.isTreeNodeClicked = true;
    				}
    			},this);
    		},
    		'click' : function(n,e){
    			if(this.enableClearValue){
    				this.triggers[0].show();
    			}
				this.setValue(n.text);
				if(this.hiddenField){
					this.setHiddenValue(Ext.value(n.id,''));
				}
    			this.fireEvent('treenodeselect',this,n,e);
    		}
		});
		
		this.tree.root.on({
			scope:this,
			'expand' : {
				fn : function(){
					if(!Ext.isDefined(this.tree.height)){//dropdown list auto height
						this.restrictHeight();
					}
				},
				single: true
			}
		});
		
		this.on({
			'beforecollapse' : {
				fn : function() {
					if(!this.isExpanded()){
			            return;
			        }
					return this.isTreeNodeClicked;
				}
			},
			'beforedestroy' : {
	            fn : function() {
	                this.purgeListeners();
	                this.tree.purgeListeners();   
	            }   
	        },
			'beforeexpand' : {
	            fn : function() {
					if(this.tree.rendered && !this.tree.getRootNode().isExpanded()){
	                	this.tree.getRootNode().expand(false,true);
	                } 
	            },   
	            single : true
	        },
			'focus' : {
				fn : function(){
					if (!this.tree.rendered && this.tplId) {
						this.tree.render(this.tplId);
	                }
				},
				single : true
			}
		});
	},
	
	// private
    initValue : function(){
        Ext.ux.form.ComboBoxTree.superclass.initValue.call(this);
        if(this.hiddenField){
            this.setHiddenValue(Ext.value(Ext.isDefined(this.hiddenValue) ? this.hiddenValue : this.value, ''));
        }
    },
	
	//private, to get this field's owner BasicForm
	getOwnerForm : function(c){
		var formPanel = this.findDirectFormOwnerCt(c);
		if(formPanel && formPanel.getForm()){
			return formPanel.getForm();
		}else{
			return null;
		}
	},
	
	//private, to find this field's direct formPanel ownerCt
	findDirectFormOwnerCt : function(c){
		if(c.ownerCt){
			if(c.ownerCt.getXType() == 'form'){
				return c.ownerCt;
			}else{
				return this.findDirectFormOwnerCt(c.ownerCt);
			}
		}else{
			return null;
		}
	},
	
	//private
	getListParent : function(){
		return document.body;
	},
	
	getParentZIndex : function(){
        var zindex;
        if (this.ownerCt){
            this.findParentBy(function(ct){
                zindex = parseInt(ct.getPositionEl().getStyle('z-index'), 10);
                return !!zindex;
            });
        }
        return zindex;
    },
	
	// private
    restrictHeight : function(){
        this.innerList.dom.style.height = '';
        var inner = this.innerList.dom,
            pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
            h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
            ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
            hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
            space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad;

        h = Math.min(h, space, this.maxListHeight);
	
        this.innerList.setHeight(h);
        this.list.beginUpdate();
        this.list.setHeight(h+pad);
        this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
        this.list.endUpdate();
    },
	
	//private
	isExpanded : function(){
        return this.list && this.list.isVisible();
    },
	
	//private
	onViewClick : function(doFocus){
		var index = this.view.getSelectedIndexes()[0];
		if(!index){
            this.collapse();
        }
        if(doFocus == true){
            this.el.focus();
        }
	},
	
	// private
    onDestroy : function(){
        Ext.destroy(
            this.resizer,
            this.view,
            this.list
        );
        Ext.destroyMembers(this, 'hiddenField');
        Ext.ux.form.ComboBoxTree.superclass.onDestroy.call(this);
    },
	
    //override
    setValue : function(val){
    	val = Ext.value(val,'');
    	if(val != ''){
    		if(this.enableClearValue){
				this.triggers[0].show();
			}
    	}else{
    		if(this.enableClearValue){
				this.triggers[0].hide();
			}
    	}
    	Ext.ux.form.ComboBoxTree.superclass.setValue.call(this,val);
    },
    
    //private
    setHiddenValue : function(val){
    	val = Ext.value(val,'');
    	if(val != ''){
    		if(this.enableClearValue){
				this.triggers[0].show();
			}
    	}else{
    		if(this.enableClearValue){
				this.triggers[0].hide();
			}
    	}
    	if(this.hiddenField){
    		this.hiddenField.value = val;
			this.value = val;
    	}
    	return this;
    },
    
    //override
    reset : function(){
        Ext.ux.form.ComboBoxTree.superclass.reset.call(this);
        if(this.hiddenField){
            this.setHiddenValue('');
        }
    },
    
	//clear value by click clear value trigger
	clearValue : function(){
		if(this.enableClearValue){
			var v = this.value;
			if(this.hiddenField){
				v = this.hiddenField.value;
				this.hiddenField.value = '';
			}
			this.setValue('');
			this.validate();
			this.fireEvent('clearvalue', this, v);
		}
    },
	
	getTree : function(){
		return this.tree;
	},
	
	//override
    getValue : function(){
        if(this.hiddenField){
            return Ext.isDefined(this.value) ? this.value : '';
        }else{
            return Ext.form.ComboBox.superclass.getValue.call(this);
        }
    },
	
	collapse : function(){
		if(!this.isExpanded()){
			return;
		}
		
		if(this.enableClearValue && Ext.value(this.value,'') != ''){
			this.triggers[0].show();
		}
		
		if(this.fireEvent('beforecollapse',this) !== false){
	        this.list.hide();
	        Ext.getDoc().un('mousewheel', this.collapseIf, this);
	        Ext.getDoc().un('mousedown', this.collapseIf, this);
	        this.fireEvent('collapse', this);
    	}
    	
    },
	
    expand : function(){
		if(this.isExpanded() || !this.hasFocus){
			return;
		}
        if(this.fireEvent('beforeexpand', this) !== false){

	        this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
	
	        // zindex can change, re-check it and set it if necessary
	        var listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
	            zindex = parseInt(Ext.fly(listParent).getStyle('z-index') ,10);
	        if (!zindex){
	            zindex = this.getParentZIndex();
	        }
	        if (zindex) {
	            this.list.setZIndex(zindex + 5);
	        }
	        
	        this.triggers[0].hide();

	        this.list.show(this.animate);
	        
	        if(Ext.isGecko2){
	            this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
	        }
	        this.mon(Ext.getDoc(), {
	            scope: this,
	            mousewheel: this.collapseIf,
	            mousedown: this.collapseIf
	        });
	        
	        this.fireEvent('expand', this);
        }
    },
	
    // private
    collapseIf : function(e){
        if(this.isExpanded() && !this.isDestroyed && !e.within(this.wrap) && !e.within(this.list)){
			this.isTreeNodeClicked = true;
            this.collapse();
        }
    },
	
	onTriggerClick : function(e){
		if(e.getTarget('#' + this.el.id, 1)){
			return;
		}
		if(this.readOnly || this.disabled){
            return;
        }
        if(this.isExpanded()){
            this.collapse();
            this.el.focus();
        }else {
			this.onFocus({});
            this.expand();
			this.restrictHeight();
			this.el.focus();
        }
	},
	
	//override, to prevent trigger hidden when set readOnly = true
    updateEditState: function(){
        if(this.rendered){
            if (this.readOnly) {
                this.el.dom.readOnly = true;
                this.el.addClass('x-trigger-noedit');
                this.mun(this.el, 'click', this.onTriggerClick, this);
            } else {
                if (!this.editable) {
                    this.el.dom.readOnly = true;
                    this.el.addClass('x-trigger-noedit');
                    this.mon(this.el, 'click', this.onTriggerClick, this);
                } else {
                    this.el.dom.readOnly = false;
                    this.el.removeClass('x-trigger-noedit');
                    this.mun(this.el, 'click', this.onTriggerClick, this);
                }
                this.trigger.setDisplayed(!this.hideTrigger);
            }
            this.onResize(this.width || this.wrap.getWidth());
        }
    }
});

Ext.reg('xcomboboxtree',Ext.ux.form.ComboBoxTree);


 具体使用如下:

items:[{
							xtype:'xcomboboxtree',
							fieldLabel:'父节点',
							enableClearValue:true,//显示清除值的trigger
							hiddenName:'foo',
							width:120,//该域的长度
							listWidth:220,//下拉框的长度
							listHeight:200,//下拉框的高度
							tree:new Ext.tree.TreePanel({
								root:new Ext.tree.AsyncTreeNode({
									id:'0',
									text:'ExtJs-3.2'
								}),
								loader:new Ext.tree.TreeLoader({
									dataUrl:'tree-data.json',
									preloadChildren:true
								}),
							}),
							listeners:{
								'treenodeselect':function(combotree,node){
									combotree.setValue(node.text);
									combotree.setHiddenValue(node.id);
								}
							}
						}]

效果:




1 楼 charsunny 2011-06-19  
谢谢分享,总算找到一个可用的了~
2 楼 jianxia801 2012-06-25  
哥们你这个确实不错;我给你扩展了一下;实现了可以设置和获得选中的树节点。添加了如下两个方法:
getSelectedNode:function(){
var nodeId=this.getValue();
var selectedNode=null;
alert(nodeId);
if(nodeId!=""){
selectedNode=this.tree.getNodeById(nodeId);
}
return selectedNode;
},
setSelectedNode:function(nodeId){
var selectedNode=this.tree.getNodeById(nodeId);
selectedNode.select();
},
主要用于设置选中树节点!
  相关解决方案