1. #1
    Sencha User j.bruni's Avatar
    Join Date
    Jun 2009
    Location
    Uberlândia, MG, Brazil
    Posts
    105
    Vote Rating
    7
    j.bruni is on a distinguished road

      0  

    Lightbulb TreePanel with tri-state checkboxes

    TreePanel with tri-state checkboxes


    I have not found a TreePanel with tri-state checkboxes the way I needed it, so I implemented it myself and I am proudly sharing with the community.


    Features:

    - If a parent node is checked / unchecked, all its child nodes are automatically checked / unchecked too.


    - If only some childs of a node are selected, the checkbox remains checked, but with a third visual state, using a darkened background.


    - A single image file defines all the three states images.


    Example, with source code:

    http://www.jbruni.com.br/extjs-tristate/


    The example page was based on this official ExtJS example:
    http://www.extjs.com/deploy/dev/exam...heck-tree.html


    Note: This implementation can surely be improved. Use it as a base and adapt it for your own needs.

  2. #2
    Sencha - Services Team Stju's Avatar
    Join Date
    Dec 2008
    Location
    Redwood city, California
    Posts
    290
    Vote Rating
    3
    Stju is on a distinguished road

      0  

    Default


    Nice.
    As a side effect - If the tree is not loaded fully from beginning then:
    1. You click on nodes checkbox
    2. It draws checked for clicked node
    3. Loading of clicked node child nodes is still in progress!
    4. Loading of child nodes finished
    5. None of child nodes are checked

    Otherwise Ecellent

  3. #3
    Sencha User j.bruni's Avatar
    Join Date
    Jun 2009
    Location
    Uberlândia, MG, Brazil
    Posts
    105
    Vote Rating
    7
    j.bruni is on a distinguished road

      0  

    Default


    Quote Originally Posted by Stju View Post
    As a side effect - If the tree is not loaded fully from beginning then:
    1. You click on nodes checkbox
    2. It draws checked for clicked node
    3. Loading of clicked node child nodes is still in progress!
    4. Loading of child nodes finished
    5. None of child nodes are checked
    1) Try changing the TreePanel "animate" setting to false and see if it works.

    Code:
    animate:false,
    2) People in another thread went deep in this not-preloaded-children issue, but I have not spent time with it because in my case the tree was small and all loaded at once. You may want to check and see how they solved this, and perhaps do the necessary adaptations in the code.

    Thanks for the feedback!
    Last edited by j.bruni; 5 May 2010 at 2:10 AM. Reason: Changed link to another thread making it point directly to the relevant post

  4. #4
    Sencha - Services Team Stju's Avatar
    Join Date
    Dec 2008
    Location
    Redwood city, California
    Posts
    290
    Vote Rating
    3
    Stju is on a distinguished road

      0  

    Default


    No... That doesn't helped.. animate was initially set as false..

  5. #5
    Sencha - Services Team Stju's Avatar
    Join Date
    Dec 2008
    Location
    Redwood city, California
    Posts
    290
    Vote Rating
    3
    Stju is on a distinguished road

      0  

    Default


    Made some modifications according to thread You sugessted! And -voila It works
    So here is updated function in case someone else needs it:
    Code:
        checkChange:function(node, checked){
            if (!this.changing)
                {
                this.changing = true;
                node.expand(true, false,function(node){
                    if (checked)
                        node.cascade( function(node){ node.getUI().toggleCheck(true) } );
                    else
                        node.cascade( function(node){ node.getUI().toggleCheck(false) } );
                    node.bubble( function(node){ if (node.parentNode) node.getUI().updateCheck() } );
                });
                this.changing = false;
                }
        }

  6. #6
    Sencha User
    Join Date
    Jul 2007
    Location
    Italy
    Posts
    134
    Vote Rating
    0
    fermo111 is on a distinguished road

      0  

    Default


    It does not work if some of the json nodes loaded have a
    Code:
    checked = true

  7. #7
    Sencha User wemerson.januario's Avatar
    Join Date
    Nov 2008
    Location
    Brazil, Goiânia
    Posts
    472
    Vote Rating
    12
    wemerson.januario will become famous soon enough

      0  

    Default Really Nice

    Really Nice


    nice work, I will test it soon in my project. Thanks
    Wemerson Januario
    Twitter:
    @januariocoder
    Email: wemerson.januario@gmail.com

    From: Goiânia, Brazil
    Ext JS Developer

  8. #8
    Sencha User
    Join Date
    Apr 2010
    Posts
    8
    Vote Rating
    0
    Balbuzar is on a distinguished road

      0  

    Default


    Nice work, but it runs only on FF

    Doesn't work on Chrome 5, Safari 5, IE 8 maybe due to the use of the 'default' keyname used on check-tree-tristate.js line 94
    PHP Code:
    uiProviders: { default: Ext.tree.TreeNodeUItristateExt.tree.TreeNodeTriStateUI 
    I can't test my solution but maybe this can fix:
    Code:
    uiProviders: { "default": Ext.tree.TreeNodeUI, tristate: Ext.tree.TreeNodeTriStateUI }

  9. #9
    Ext User
    Join Date
    May 2009
    Posts
    6
    Vote Rating
    0
    andyghiuta is on a distinguished road

      0  

    Default Example not available

    Example not available


    Can you post the example on another page, maybe upload the source somewhere? because your page is not working anymore.
    Thanks!

    LE: works now, Thank you very much!

  10. #10
    Sencha - Services Team Stju's Avatar
    Join Date
    Dec 2008
    Location
    Redwood city, California
    Posts
    290
    Vote Rating
    3
    Stju is on a distinguished road

      0  

    Thumbs up


    This is modified version of original j.bruni version.
    Things changed:
    1.It is possible now to load tree content from JSON and mark node as 'checked':true. Be sure to pass boolean value not the string, otherwise checkbox will remain unchecked! Remarking partial node status is done by script, so there is no need to send info about partial status!
    2. Changed css, to show correctly in Webkit browsers.
    3. Resolved issue with not preloaded children nodes.
    4. Comment: specify only
    Code:
    uiProviders: {tristate: Ext.tree.TreeNodeTriStateUI }
    , as UI provider!

    Tested against 3.2.2 (but should be okay with 3.2.x). FF3.6.3, Opera 10.54, Safari 5.
    CSS:
    Code:
    .styledCheckbox {
        height: 13px;
        width: 13px;
        filter: alpha(opacity=0.0);
        opacity: 0.0;
        outline: 0;
    }
    
    .styledCheckboxWrap {
        position: relative;
        background: url('checkboxes.gif') no-repeat top left;
        width: 13px;
        height: 13px;
        margin: 3px;
    }
    
    .ext-webkit .styledCheckboxWrap {
        padding-bottom:1px;
    }
    .wrapChecked{ background-position: 0px -13px; }
    
    .wrapPartial{ background-position: 0px -26px; }
    TreeNodeTriStateUI.js
    Code:
    Ext.tree.TreeNodeTriStateUI = function() {
        Ext.tree.TreeNodeTriStateUI.superclass.constructor.apply(this, arguments);
        this.partial = false;
    };
    
    Ext.extend(Ext.tree.TreeNodeTriStateUI, Ext.tree.TreeNodeUI, {
        renderElements : function(n, a, targetNode, bulkRender){
            this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
    
            var cb = Ext.isBoolean(a.checked),
                nel,
                href = a.href ? a.href : Ext.isGecko ? "" : "#",
                buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',
                '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
                '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
                '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
                cb ? ('<span class="styledCheckboxWrap '+ (a.checked ? ' wrapChecked' :'')+'"><input class="x-tree-node-cb styledCheckbox" type="checkbox" ' +
                        (a.checked ? 'checked="checked" />' : '/>') + '</span>' ) : '', //modified for load checked nodes feature
                '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
                 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
                '<ul class="x-tree-node-ct" style="display:none;"></ul>',
                "</li>"].join('');
    
            if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
                this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
            }else{
                this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
            }
    
            this.elNode = this.wrap.childNodes[0];
            this.ctNode = this.wrap.childNodes[1];
            var cs = this.elNode.childNodes;
            this.indentNode = cs[0];
            this.ecNode = cs[1];
            this.iconNode = cs[2];
            var index = 3;
            if(cb){
                this.checkbox = cs[3].firstChild;
                // fix for IE6
                this.checkbox.defaultChecked = this.checkbox.checked;
                index++;
            }
            this.anchor = cs[index];
            this.textNode = cs[index].firstChild;
            //updating partial nodes
            if (a.checked)  n.bubble( function(n){ if (n.parentNode) n.getUI().updateCheck(true) } );//pass in true -> inform, that this is loaded node!
        },
    
        toggleCheck: function(value, partial, isLoad){
            var cb = this.checkbox;
            if(cb){
                cb.checked = (value === undefined ? !cb.checked : value);
                cb.parentNode.className = 'styledCheckboxWrap' + (cb.checked ? (partial ? ' wrapPartial' : ' wrapChecked') : '');
                this.partial = cb.checked && partial;
                if(!isLoad) this.onCheckChange();
            }
        },
    
        updateCheck: function(isLoad){
            if ( this.node.childNodes.length == 0 )
                return;
            this.partial = 0;
            Ext.each( this.node.childNodes, function(item){
                var ui = item.getUI();
                if (ui.isChecked()) {
                    this.partial++;
                    if (ui.partial)
                    {
                        this.toggleCheck(true,true,isLoad);
                        return false;
                    }
                }
            }, this );
            if ( this.partial !== true )
                this.toggleCheck( this.partial > 0, this.partial < this.node.childNodes.length, isLoad );
        }
    });
    And in Your code:
    Code:
    this.myTreeStateTree = new Ext.tree.TreePanel({
            height:270,
            autoScroll: true,
            animate: false,
            containerScroll: true,
            loader: new Ext.tree.TreeLoader({
                directFn: XXX.treeLoadFunction,
                paramOrder: ['compid'], //param order
                baseParams: { //parameters to send along with tree load
                    compid : 0 // specify default value
                },
                uiProviders: {tristate: Ext.tree.TreeNodeTriStateUI }
            }),
            root: {
                nodeType: 'async',
                text: 'Node title goes here',
                draggable: false,
                isTarget:false,
                editable :false,
                id: 'src',
                uiProvider:false,
                iconCls:"ico-application-share"
            },
            listeners:{
                scope:this,
                checkchange:{
                    fn:this.checkChange
                }
            }
        });
    .
    .
    .
    checkChange:function(node, checked){
            if (!this.changing)
                {
                this.changing = true;
                node.expand(true, false,function(node){
                    if (checked)
                        node.cascade( function(node){ node.getUI().toggleCheck(true) } );
                    else
                        node.cascade( function(node){ node.getUI().toggleCheck(false) } );
                    node.bubble( function(node){ if (node.parentNode) node.getUI().updateCheck(false) } );
                });
                this.changing = false;
                }
        },
    .
    .