ExtJS带复选框的下拉树对复选框中半选状态的实现

Extjs基本组件:带复选框的下拉树

        首先,我们对于ExtJs实现的带复选框的下拉树应该有一个简单的了解,从下面的例子中可以看出,treepanel是通过节点中checked的值(true,false)来实现复选框的两种状态(勾选,不选)的。

代码段:

Ext.create('Ext.window.Window', {
	title: 'Test',
    height: 230,
    width: 400,
    layout: 'fit',
	autoShow:true,
    items: {
        xtype: 'treepanel',
        store: Ext.create('Ext.data.TreeStore', {
		    root: {
		        expanded: true,
		        children: [
		            { text: 'child1', leaf: true, checked: false},
		            { text: 'child2', expanded: true, checked: false, children: [
		                { text: 'child2-1', expanded: true, checked: false, children:[
		                 	{ text: 'child2-1-1', leaf: true, checked: true},
		                 	{ text: 'child2-1-2', leaf: true, checked: false}
		            	] },
		            	{ text: 'child2-2', leaf: true, checked: true}
		            ] },
		            { text: 'child3', leaf: true, checked: false }
		        ]
		    }
		}),
    	rootVisible: false
 	}
});

展示图:


半选的实现原理

        其实实现checkbox半选很简单,首先我们应该知道,显示在界面上的效果不过就是一张图片而已,而checked的值(true/false)就像两个开关一样,随着值得改变,由css样式去更改图片;而我们现在要做的就是在此基础上再加一个开关(indeterminate),优先级比checked高,当indeterminate = true时,由css样式去更改我们自己的半选图片,indeterminate = false时,由checked去负责复选框的选中和不选两种状态。

        需要注意的一点就是,由于点击半选的复选框时,复选框需要处于不选状态中,所以当复选框处于半选状态时,checked此时应该等于true。

       

ok,从上面的原理可以看出我们实现半选的几个难点:

  1. 随着checkbox的点击,正确的改变节点中checked和indeterminate的值
  2. 根据checked和indeterminate的值如何修改样式

那么,该怎么解决呢?

自定义组件

        组件的自定义封装是Extjs的基本功了,我们需要封装一个组件来实现我们上述的难点1

Ext.define('component.CheckTree', {
	extend: 'Ext.tree.Panel',
	requires: [
		'Ext.data.TreeStore'
	],
	xtype: 'checktree',
	store: {},
	rootVisible: false,
	useArrows: true,
	frame: true,
	title: 'Check Tree',
	header: false,
	bufferedRenderer: false,
	animate: true,
	minHeight: 200,
	maxHeight: 200,
	bodyStyle: 'overflow-x:no; overflow-y:auto',
	SELSTATUS: {
		NONE: 0,      //没有选中
		HALFSEL: 1,    //半选
		ALLSEL: 2      //全选
	},

	listeners: {
		checkchange: function(node, checked) {
			this.setChildChecked(node, checked);
			this.setParentChecked(node, checked);
		}
	},

	initComponent: function() {
		Ext.apply(this, {
			store: this.store,
		});
		this.callParent();
	},
	
	//遍历所有子节点,全选或全不选所有子节点,indeterminate设置为false
	setChildChecked: function(node, checked) {
		var me = this;
		node.expand();
		node.set('indeterminate', false);
		node.data.checked = !checked;
		node.set('checked', checked);
		if (node.hasChildNodes()) {
			node.eachChild(function(child) {
				this.setChildChecked(child, checked);
			}, me)
		}
	},
	
	//遍历所有的父节点,设置复选框的三种状态  
    //注意:checked的set方法只有在当前值与要改变的值不同时,才回去修改样式
	setParentChecked: function(node, checked, selStatus) {
		var me = this,
			parentNode = node.parentNode,
			isHalfSel = false, //半选
			isAllSel = false, //全选
			isNone = true,
			indeterminate,
			checkedArr = [],
			indexT, indexF;
			
		if (parentNode) {
			if(typeof(selStatus) == 'number'){
				switch (selStatus){
					case 0:
						node.set('indeterminate', false);
						node.data.checked = true;
						node.set('checked', false);
						me.setParentChecked(node, checked, null);
						break;
					case 1:
						node.set('indeterminate', true);
						node.data.checked = false;
						node.set('checked', true);
						me.setParentChecked(parentNode, checked, me.SELSTATUS['HALFSEL']);
						break;
					case 2:
						node.set('indeterminate', false);
						node.data.checked = false;
						node.set('checked', true);
						me.setParentChecked(node, checked, null);
						break;
					default:
						break;
				}
			}else{
				selStatus = null;
				parentNode.eachChild(function(childnode) {
					indeterminate = childnode.get('indeterminate');
					if (typeof(indeterminate) == 'boolean' && indeterminate) {
						selStatus = me.SELSTATUS['HALFSEL'];
					}
					if(!isHalfSel){
						checkedArr.push(childnode.get('checked'));
					}
				});
				if(!selStatus){
					indexT = checkedArr.indexOf(true);
					indexF = checkedArr.indexOf(false);
					switch (true){
						case  -1 == indexT:
							selStatus = me.SELSTATUS['NONE'];
							break;
						case  -1 == indexF:
							selStatus = me.SELSTATUS['ALLSEL'];
							break;
						default:
							selStatus = me.SELSTATUS['HALFSEL'];
							break;
					}
				}
				me.setParentChecked(parentNode, checked, selStatus);
			}
		}
	}
});

样式的添加与控制

        已经来到了最后一步,黎明的曙光即将到来。

        首先,我们需要明确几点:

            1.  只有当node.set('checked', true/false),并且node.get('checked')的值与改变的值不同时,才会去修改样式;

            2.  当复选框为不选状态的时候,checkboxCls = ‘.x-tree-checkbox’ ,当为选中的时候,checkboxCls = ‘.x-tree-checkbox  .x-tree-checkbox-checked’ 

.x-tree-checkbox-checked {
    background-position: 0 -15px;
}

.x-tree-checkbox {
    margin-right: 4px;
    top: 5px;
    width: 15px;
    height: 15px;
    background-image: url(images/form/checkbox.png);
}

            

ok,说了这么多,相信大家的思路应该比较清晰了,通过set方法进入源码,可以发现可以在Ext.tree.Column中的treeRenderer方法添加我们需要的样式,那么,我们可以重写这个方法,其中.x-tree-checkbox-indeterminate是我们实现半选的样式

代码段:

Ext.define('Ext.overrides.tree.Column', {
	override: 'Ext.tree.Column',
	
	treeRenderer: function(value, metaData, record, rowIdx, colIdx, store, view) {
        var me = this,
            cls = record.get('cls'),
            rendererData;
        // The initial render will inject the cls into the TD's attributes.
        // If cls is ever *changed*, then the full rendering path is followed.
        if (metaData && cls) {
            metaData.tdCls += ' ' + cls;
        }
        rendererData = me.initTemplateRendererData(value, metaData, record, rowIdx, colIdx, store, view);
        
        //导航树复选框的半选状态实现
        var indeterminate = record.get('indeterminate');
        if('boolean' === typeof(indeterminate) && indeterminate){
        	rendererData.checkboxCls += ' x-tree-checkbox-indeterminate';
        }
        
        return me.getTpl('cellTpl').apply(rendererData);
    }
});

            

当然,.x-tree-checkbox-indeterminate是不存在的,是我们自己需要的,要达到我们的目的,就要让组件在渲染时,能够找到它,因此,我们可以在主题中给该组件添加这个样式,并把.x-tree-checkbox中的图片文件改成我们需要的

.x-tree-checkbox {
    margin-right: 4px;
    top: 5px;
    width: 15px;
    height: 15px;
    background-image: url(images/form/checkbox1.png);
}

.x-tree-checkbox-indeterminate {
    background-position: 0 -30px;
}

半选实现

代码段:

Ext.create('Ext.window.Window', {
	title: 'Test',
    height: 230,
    width: 400,
    layout: 'fit',
	autoShow:true,
    items: {
        xtype: 'checktree',
        store: Ext.create('Ext.data.TreeStore', {
		    root: {
		        expanded: true,
		        children: [
		            { text: 'child1', leaf: true, checked: false, indeterminate: false},
		            { text: 'child2', expanded: true, checked: false, indeterminate: false, children: [
		                { text: 'child2-1', expanded: true, checked: false, indeterminate: false, children:[
		                 	{ text: 'child2-1-1', leaf: true, checked: false, indeterminate: false},
		                 	{ text: 'child2-1-2', leaf: true, checked: false, indeterminate: false}
		            	] },
		            	{ text: 'child2-2', leaf: true, checked: false, indeterminate: false}
		            ] },
		            { text: 'child3', leaf: true, checked: false, indeterminate: false}
		        ]
		    }
		}),
    	rootVisible: false
 	}
});

点击后的效果:

猜你喜欢

转载自my.oschina.net/u/2449363/blog/777305