1. #1
    Sencha User
    Join Date
    Jul 2009
    Posts
    30
    Vote Rating
    7
    da_bar is on a distinguished road

      0  

    Default Ext.ux.TreeCombo

    Ext.ux.TreeCombo


    Tree combo using 'Ext.data.TreeStore',

    Examples: http://extjs.dariofilkovic.com/

    Features:
    - single select
    - multiselect
    - children select
    - checkbox tree
    - can be used in form
    - 'itemclick' event to capture click, and selected records

    example:

    Ext.create('Ext.ux.TreeCombo',
    {
    store: someStore,
    selectChildren: false
    canSelectFolders: false
    });

    Additional info in code

    Code:
    /*
    Tree combo
    Use with 'Ext.data.TreeStore'
    
    
    If store root note has 'checked' property tree combo becomes multiselect combo (tree store must have records with 'checked' property)
    
    
    Has event 'itemclick' that can be used to capture click
    
    
    Options:
    selectChildren - if set true and if store isn't multiselect, clicking on an non-leaf node selects all it's children
    canSelectFolders - if set true and store isn't multiselect clicking on a folder selects that folder also as a value
    
    
    Use:
    
    
    single leaf node selector:
    selectChildren: false
    canSelectFolders: false
    - this will select only leaf nodes and will not allow selecting non-leaf nodes
    
    
    single node selector (can select leaf and non-leaf nodes)
    selectChildren: false
    canSelectFolders: true
    - this will select single value either leaf or non-leaf
    
    
    children selector:
    selectChildren: true
    canSelectFolders: true
    - clicking on a node will select it's children and node, clicking on a leaf node will select only that node
    
    
    This config:
    selectChildren: true
    canSelectFolders: false
    - is invalid, you cannot select children without node
    
    
    */
    Ext.define('Ext.ux.TreeCombo',
    {
    	extend: 'Ext.form.field.Picker',
    	alias: 'widget.treecombo',
    	tree: false,
    	constructor: function(config)
    	{
    		this.addEvents(
    		{
    			"itemclick" : true
    		});
    
    
    		this.listeners = config.listeners;
    		this.callParent(arguments);
    	},
    	records: [],
    	recursiveRecords: [],
    	ids: [],
    	selectChildren: true,
    	canSelectFolders: true,
    	multiselect: false,
    	displayField: 'text',
    	valueField: 'id',
    	treeWidth: 300,
    	matchFieldWidth: false,
    	treeHeight: 400,
    	masN: 0,
    	recursivePush: function(node, setIds)
    	{
    		var	me = this;
    
    
    		me.addRecRecord(node);
    		if(setIds) me.addIds(node);
    		
    		node.eachChild(function(nodesingle)
    		{
    			if(nodesingle.hasChildNodes() == true)
    			{
    				me.recursivePush(nodesingle, setIds);
    			}
    			else
    			{
    				me.addRecRecord(nodesingle);
    				if(setIds) me.addIds(nodesingle);
    			}
    		});
    	},
    	recursiveUnPush: function(node)
    	{
    		var	me = this;
    		me.removeIds(node);
    		
    		node.eachChild(function(nodesingle)
    		{
    			if(nodesingle.hasChildNodes() == true)
    			{
    				me.recursiveUnPush(nodesingle);
    			}
    			else me.removeIds(nodesingle);
    		});
    	},
    	addRecRecord: function(record)
    	{
    		var	me = this;
    
    
    		for(var i=0,j=me.recursiveRecords.length;i<j;i++)
    		{
    			var item = me.recursiveRecords[i];
    			if(item)
    			{
    				if(item.getId() == record.getId()) return;
    			}
    		}
    		me.recursiveRecords.push(record);
    	},
    	afterLoadSetValue: false,
    	setValue: function(valueInit)
    	{
    		if(typeof valueInit == 'undefined') return;
    		
    		var	me = this,
    			tree = this.tree,
    			values = (valueInit == '') ? [] : valueInit.split(','),
    			valueFin = [];
    			
    		inputEl = me.inputEl;
    
    
    		if(tree.store.isLoading())
    		{
    			me.afterLoadSetValue = valueInit;
    		}
    
    
    		if(inputEl && me.emptyText && !Ext.isEmpty(values))
    		{
    			inputEl.removeCls(me.emptyCls);
    		}
    
    
    		if(tree == false) return false;
    		
    		var node = tree.getRootNode();
    		if(node == null) return false;
    		
    		me.recursiveRecords = [];
    		me.recursivePush(node, false);
    		
    		me.records = [];
    		Ext.each(me.recursiveRecords, function(record)
    		{
    			var	id = record.get(me.valueField),
    				index = values.indexOf(''+id);
    		
    			if(me.multiselect == true) record.set('checked', false);
    			
    			if(index != -1)
    			{
    				valueFin.push(record.get(me.displayField));
    				if(me.multiselect == true) record.set('checked', true);
    				me.addRecord(record);
    			}
    		});
    
    
    		me.value = valueInit;
    		me.setRawValue(valueFin.join(', '));
    		
    		me.checkChange();
    		me.applyEmptyText();
    		return me;
    	},
    	getValue: function() 
    	{
    		return this.value;
    	},
    	getSubmitValue: function()
    	{
    		return this.value;
    	},
    	checkParentNodes: function(node)
    	{
    		if(node == null) return;
    		
    		var	me = this,
    			checkedAll = true;
    
    
    		node.eachChild(function(nodesingle)
    		{
    			var	id = nodesingle.getId(),
    				index = me.ids.indexOf(''+id);
    				
    			if(index == -1) checkedAll = false;
    		});
    		
    		if(checkedAll == true)
    		{
    			me.addIds(node);
    			me.checkParentNodes(node.parentNode);
    		}
    		else
    		{
    			me.removeIds(node);
    			me.checkParentNodes(node.parentNode);
    		}
    	},
    	initComponent: function() 
    	{
    		var	me = this;
    		
    		me.tree = Ext.create('Ext.tree.Panel',
    		{
    			alias: 'widget.assetstree',
    			hidden: true,
    			minHeight: 300,
    			rootVisible: (typeof me.rootVisible != 'undefined') ? me.rootVisible : true,
    			floating: true,
    			useArrows: true,
    			width: me.treeWidth,
    			autoScroll: true,
    			height: me.treeHeight,
    			store: me.store,
    			listeners:
    			{
    				load: function(store, records)
    				{
    					if(me.afterLoadSetValue != false)
    					{
    						me.setValue(me.afterLoadSetValue);
    					}
    				},
    				itemclick:  function(view, record, item, index, e, eOpts)
    				{
    					me.itemTreeClick(view, record, item, index, e, eOpts, me)
    				}
    			}
    		});
    		
    		if(me.tree.getRootNode().get('checked') != null) me.multiselect = true;
    		
    		this.createPicker = function()
    		{
    			var	me = this;
    			return me.tree;
    		};
    		
    		this.callParent(arguments);
    	},
    	addIds: function(record)
    	{
    		var	me = this;
    		
    		if(me.ids.indexOf(''+record.getId()) == -1) me.ids.push(''+record.get(me.valueField));
    	},
    	removeIds: function(record)
    	{
    		var	me = this,
    			index = me.ids.indexOf(''+record.getId());
    			
    		if(index != -1)
    		{
    			me.ids.splice(index, 1);
    		}
    	},
    	addRecord: function(record)
    	{
    		var	me = this;
    
    
    		for(var i=0,j=me.records.length;i<j;i++)
    		{
    			var item = me.records[i];
    			if(item)
    			{
    				if(item.getId() == record.getId()) return;
    			}
    		}
    		me.records.push(record);
    	},
    	removeRecord: function(record)
    	{
    		var	me = this;
    
    
    		for(var i=0,j=me.records.length;i<j;i++)
    		{
    			var item = me.records[i];
    			if(item && item.getId() == record.getId()) delete(me.records[i]);
    		}
    	},
    	itemTreeClick: function(view, record, item, index, e, eOpts, treeCombo)
    	{
    		var	me = treeCombo,
    			checked = !record.get('checked');//it is still not checked if will be checked in this event
    		
    		if(me.multiselect == true) record.set('checked', checked);//check record
    		
    		var node = me.tree.getRootNode().findChild(me.valueField, record.get(me.valueField), true);
    		if(node == null) 
    		{
    			if(me.tree.getRootNode().get(me.valueField) == record.get(me.valueField)) node = me.tree.getRootNode();
    			else return false;
    		}
    		
    		if(me.multiselect == false) me.ids = [];
    		
    		//if it can't select folders and it is a folder check existing values and return false
    		if(me.canSelectFolders == false && record.get('leaf') == false)
    		{
    			me.setRecordsValue(view, record, item, index, e, eOpts, treeCombo);
    			return false;
    		}
    		
    		//if record is leaf
    		if(record.get('leaf') == true) 
    		{
    			if(checked == true)
    			{
    				me.addIds(record);
    			}
    			else
    			{
    				me.removeIds(record);
    			}
    		}
    		else //it's a directory
    		{			
    			me.recursiveRecords = [];
    			if(checked == true)
    			{
    				if(me.multiselect == false)
    				{
    					if(me.canSelectFolders == true) me.addIds(record); 
    				}
    				else
    				{
    					if(me.canSelectFolders == true)
    					{
    						me.recursivePush(node, true);
    					}
    				}
    			}
    			else
    			{
    				if(me.multiselect == false)
    				{
    					if(me.canSelectFolders == true) me.recursiveUnPush(node);
    					else me.removeIds(record);
    				}
    				else me.recursiveUnPush(node);
    			}
    		}
    		
    		//this will check every parent node that has his all children selected
    		if(me.canSelectFolders == true && me.multiselect == true) me.checkParentNodes(node.parentNode);
    		
    		me.setRecordsValue(view, record, item, index, e, eOpts, treeCombo);
    	},
    	fixIds: function()
    	{
    		var me = this;
    		
    		for(var i=0,j=me.ids.length;i<j;i++)
    		{
    			if(me.ids[i] == 'NaN') me.ids.splice(i, 1);
    		}
    	},
    	setRecordsValue: function(view, record, item, index, e, eOpts, treeCombo)
    	{
    		var	me = treeCombo;
    		
    		me.fixIds();
    		
    		me.setValue(me.ids.join(','));
    
    
    		me.fireEvent('itemclick', me, record, item, index, e, eOpts, me.records, me.ids);
    
    
    		if(me.multiselect == false) me.onTriggerClick();
    	}	
    });
    Last edited by da_bar; 2 Jan 2013 at 11:35 PM. Reason: Multiple fixes

  2. #2
    Sencha - Support Team scottmartin's Avatar
    Join Date
    Jul 2010
    Location
    Houston, Tx
    Posts
    9,197
    Vote Rating
    482
    scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future

      0  

    Default


    Thank you for the contribution!

    Do you have a demo we can see?

    Regards,
    Scott.

  3. #3
    Sencha User
    Join Date
    Jul 2009
    Posts
    30
    Vote Rating
    7
    da_bar is on a distinguished road

      0  

  4. #4
    Sencha User
    Join Date
    Mar 2008
    Posts
    55
    Vote Rating
    10
    andong will become famous soon enough

      0  

    Default


    Are there any packed examples and docs?

  5. #5
    Touch Premium Member liuxing_sc's Avatar
    Join Date
    Aug 2011
    Location
    China
    Posts
    38
    Vote Rating
    0
    liuxing_sc is on a distinguished road

      0  

    Default


    @da_bar
    if IE, if you use tree with checkbox, you'll find a bug. just click on the check box not the text label, you cannot get the value

    under IE, if you click on checkbox , event should be
    function
    checkchange:function(node, checked, eOpts )

    can you make some adjustment? thank you

  6. #6
    Sencha User
    Join Date
    Mar 2012
    Posts
    10
    Vote Rating
    0
    EdGarciaC is on a distinguished road

      0  

    Default


    How can i do the combo not set the records of non leaf nodes?

  7. #7
    Sencha User
    Join Date
    Oct 2011
    Posts
    5
    Vote Rating
    0
    AiAcc is on a distinguished road

      0  

    Default


    I work it well.
    remove the console.log
    and I hit a problem
    if click the checkbox in the tree node will not work.
    because the checkchange event will before the itemclick event.
    I add this
    PHP Code:
    checkchange : function(nodechecked opt) {                    //hack for itemclick event                    node.set("checked", !checked);                }, 

  8. #8
    Sencha Premium Member
    Join Date
    Dec 2010
    Posts
    24
    Vote Rating
    4
    demotics2002 is on a distinguished road

      0  

    Default


    Im not good at extjs, so I'll just ask if someone could help me alter the above extension such that it will not show the icons of all the items in the tree, and also the tree gets filtered while I type something in the field?

  9. #9
    Ext JS Premium Member
    Join Date
    Jul 2012
    Posts
    2
    Vote Rating
    0
    kleja04 is on a distinguished road

      0  

    Default


    Quote Originally Posted by demotics2002 View Post
    Im not good at extjs, so I'll just ask if someone could help me alter the above extension such that it will not show the icons of all the items in the tree, and also the tree gets filtered while I type something in the field?
    dump

  10. #10
    Sencha Premium Member tempvalue's Avatar
    Join Date
    Apr 2012
    Location
    istanbul
    Posts
    22
    Vote Rating
    0
    tempvalue is on a distinguished road

      0  

    Default


    Hi da_bar,

    Thanks for this useful plugin.Actually it should be added in ExtJS ComboBox component in next versions.
    Here are some changes that I made to work the component fine with my ExtJS Version 4.0.7:

    1)As it is stated above(liuxing_sc), checked property is already called before itemClick. Intead of changing checkChange event i just change toggle checked controls.(false=>true)

    2)Two of the examples that you showed in http://extjs.dariofilkovic.com/ needs Ext.ux.CheckCombo which is another nice plugin published by you: http://www.sencha.com/forum/showthre....ux.CheckCombo

    3)Lastly, if i dont want to select children when folder is selected, i can configure it via selectChildren, but reverse is not configured. If i select all children one by one, folder is automatically selected. If i close folder selection then i cannot select folder anymore. In my case, tree is filled with people organization which means i can select several people in several level but no person should be selected automatically. I just added a new property autoSelectParent, and changed the control in itemClick like below:
    if (me.autoSelectParent == true) me.checkParentNodes(node.parentNode);