1. #1
    Ext JS Premium Member CutterBl's Avatar
    Join Date
    Aug 2007
    Location
    Nashville, TN
    Posts
    237
    Vote Rating
    0
    CutterBl is on a distinguished road

      0  

    Default Extending the RowExpander Plugin

    Extending the RowExpander Plugin


    I've just put up a post on my blog about Extending the RowExpander Plugin, complete with the source and a link to the example page. Basically, I wanted the ability to pass a new argument into the rowexpander, a function reference to be called on expansion. I wanted the ability to populate the expanded area, at the time of expansion, with an Ajax call. Afterwards I showed it to Rey [Bango], who said "get an example up and get it on the forums." So here I am.

    You can get the full source of the example from the post, but here's the plugin itself:

    Code:
    /*
     * Ext JS Library 2.0
     * Copyright(c) 2006-2007, Ext JS, LLC.
     * licensing@extjs.com
     * 
     * http://extjs.com/license
     * 
     * MODIFIED: SGB [12.12.07]
     * Added support for a new config option, remoteDataMethod,
     * including getter and setter functions, and minor mods
     * to the beforeExpand and expandRow functions
     */
    
    Ext.grid.RowExpander = function(config){
        Ext.apply(this, config);
        Ext.grid.RowExpander.superclass.constructor.call(this);
    
        if(this.tpl){
            if(typeof this.tpl == 'string'){
                this.tpl = new Ext.Template(this.tpl);
            }
            this.tpl.compile();
        }
    
        this.state = {};
        this.bodyContent = {};
    
        this.addEvents({
            beforeexpand : true,
            expand: true,
            beforecollapse: true,
            collapse: true
        });
    };
    
    Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
        header: "",
        width: 20,
        sortable: false,
        fixed:true,
        dataIndex: '',
        id: 'expander',
        lazyRender : true,
        enableCaching: true,
    
        getRowClass : function(record, rowIndex, p, ds){
            p.cols = p.cols-1;
            var content = this.bodyContent[record.id];
            if(!content && !this.lazyRender){
                content = this.getBodyContent(record, rowIndex);
            }
            if(content){
                p.body = content;
            }
            return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
        },
    
        init : function(grid){
            this.grid = grid;
    
            var view = grid.getView();
            view.getRowClass = this.getRowClass.createDelegate(this);
    
            view.enableRowBody = true;
    
            grid.on('render', function(){
                view.mainBody.on('mousedown', this.onMouseDown, this);
            }, this);
        },
    
        getBodyContent : function(record, index){
            if(!this.enableCaching){
                return this.tpl.apply(record.data);
            }
            var content = this.bodyContent[record.id];
            if(!content){
                content = this.tpl.apply(record.data);
                this.bodyContent[record.id] = content;
            }
            return content;
        },
    	// Setter and Getter methods for the remoteDataMethod property
    	setRemoteDataMethod : function (fn){
    		this.remoteDataMethod = fn;
    	},
    	
    	getRemoteDataMethod : function (record, index){
    		if(!this.remoteDataMethod){
    			return;
    		}
    			return this.remoteDataMethod.call(this,record,index);
    	},
    
        onMouseDown : function(e, t){
            if(t.className == 'x-grid3-row-expander'){
                e.stopEvent();
                var row = e.getTarget('.x-grid3-row');
                this.toggleRow(row);
            }
        },
    
        renderer : function(v, p, record){
            p.cellAttr = 'rowspan="2"';
            return '<div class="x-grid3-row-expander">&#160;</div>';
        },
    
        beforeExpand : function(record, body, rowIndex){
            if(this.fireEvent('beforexpand', this, record, body, rowIndex) !== false){
                // If remoteDataMethod is defined then we'll need a div, with a unique ID,
                //  to place the content
    			if(this.remoteDataMethod){
    				this.tpl = new Ext.Template("<div id='remData" + rowIndex + "' class='rem-data-expand'><\div>");
    			}
    			if(this.tpl && this.lazyRender){
                    body.innerHTML = this.getBodyContent(record, rowIndex);
                }
    			
                return true;
            }else{
                return false;
            }
        },
    	
    	toggleRow : function(row){
            if(typeof row == 'number'){
                row = this.grid.view.getRow(row);
            }
            this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
        },
    
        expandRow : function(row){
            if(typeof row == 'number'){
                row = this.grid.view.getRow(row);
            }
            var record = this.grid.store.getAt(row.rowIndex);
            var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
            if(this.beforeExpand(record, body, row.rowIndex)){
                this.state[record.id] = true;
                Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
               	if(this.fireEvent('expand', this, record, body, row.rowIndex) !== false){
    				//  If the expand event is successful then get the remoteDataMethod
    				this.getRemoteDataMethod(record,row.rowIndex);
    			}
            }
        },
    
        collapseRow : function(row){
            if(typeof row == 'number'){
                row = this.grid.view.getRow(row);
            }
            var record = this.grid.store.getAt(row.rowIndex);
            var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
            if(this.fireEvent('beforcollapse', this, record, body, row.rowIndex) !== false){
                this.state[record.id] = false;
                Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
                this.fireEvent('collapse', this, record, body, row.rowIndex);
            }
        }
    });
    After that all you need is a function, and the config to point to it:

    Code:
    //	FUNCTION: getMyStuff
    	//	@param	record		:	actual record in grid row
    	//	@param	index		:	row index calling the function
    	function getMyStuff(record,index){
    		//	Using JQuery to 'load' the expanded row with content pulled remotely
    		$('#remData'+index).load('tester.html');
    	};
    
    var expander = new xg.RowExpander({
            remoteDataMethod : getMyStuff
        });
    --
    Steve "Cutter" Blades
    Adobe Community Professional - ColdFusion
    Adobe Certified Professional - Advanced Macromedia ColdFusion MX 7 Developer

    _____________________________
    Blog: Cutter's Crossing

    Co-Author "Learning Ext JS 3.2"

  2. #2
    Sencha Premium Member
    Join Date
    Sep 2007
    Posts
    23
    Vote Rating
    1
    rkrishna_1975 is on a distinguished road

      0  

    Default


    I have to test this out... but wow... This is very similar to what I had in mind. I may still want to have a container (so that I can load what ever I want there, like say a remote component or another grid etc..) in the expanded section but this is the right direct for it.
    Ramki

  3. #3
    Ext JS Premium Member CutterBl's Avatar
    Join Date
    Aug 2007
    Location
    Nashville, TN
    Posts
    237
    Vote Rating
    0
    CutterBl is on a distinguished road

      0  

    Default It's in there

    It's in there


    Thanks Ramki. Actually the call to the remote method will automatically create a container object in the expanded area. There's more info in the blog posting.
    --
    Steve "Cutter" Blades
    Adobe Community Professional - ColdFusion
    Adobe Certified Professional - Advanced Macromedia ColdFusion MX 7 Developer

    _____________________________
    Blog: Cutter's Crossing

    Co-Author "Learning Ext JS 3.2"

  4. #4
    Sencha Premium Member
    Join Date
    Sep 2007
    Posts
    23
    Vote Rating
    1
    rkrishna_1975 is on a distinguished road

      0  

    Default


    Quote Originally Posted by CutterBl View Post
    Thanks Ramki. Actually the call to the remote method will automatically create a container object in the expanded area. There's more info in the blog posting.
    Wow.... That is great. I will play with this sometime today.
    Ramki

  5. #5
    Sencha Premium Member Iveco's Avatar
    Join Date
    Aug 2007
    Location
    Germany
    Posts
    70
    Vote Rating
    1
    Iveco is on a distinguished road

      0  

    Default Thanks

    Thanks


    Thank you very much Cutter for this plugin,
    but I am in need of some more functionality and right now I don't know how to do this. (Never coded a plugin yet, but the rowExpander is that nice I really want to have it)

    Two things are missing:

    1) I attached a 'celldblclick' event to the grid, thought when the row gets expanded, the visible expanded area doesn't have this event, how can I also attach the celldblclick event to the rowExpander's area for that row?

    2) When I have a row expanded and showing the rowExpander, meanwhile updating the grid's datastore (though refreshing the grid), it will show all rows collapsed, even if some of them before the update expanded but the ICON's doesn't get changed. I tried adding an event to the datastore, which then will check all rowExpander.state for that row and replaceClass the Icon but the problem is state only stores the id of the row not the rowIndex itself.

    Code:
    ds.reload(	// reload the datastore
    			{
    				callback : function(r, options, success)
    				{
    					if(success)
    					{
    						var expander = XXX.getExpander();
    						// find all collapsed RowExpander because they won't reset them theirself when the Grid/Store get's updated
    						if(expander.state)
    						{
    							for(row in expander.state)
    							{
    								if(expander.state[row])		// when it was collapsed while updating, it will still show the wrong icon, fix this
    								{
    					         	expander.state[row] = false;
           							// NEED ROWINDEX HERE NOT ROW!
           							Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');									
    								}
    							}
    						}
    					}

    Please someone help me!

    Regards,

    Iveco

  6. #6
    Ext User
    Join Date
    Jul 2007
    Posts
    4
    Vote Rating
    0
    meteorbites is on a distinguished road

      0  

    Default RowExpander

    RowExpander


    I find this more convenient
    Code:
    Ext.get('remData'+index).load({
                url: "tester.html",
                scripts: true,
                params: "param1=foo&param2=bar",
                text: "Loading Detailed Information..."
            });
    than
    Code:
    $('#remData'+index).load('tester.html');
    Great plugin..

    thanks
    Last edited by mystix; 4 Jan 2008 at 12:05 PM. Reason: use [code][/code] tags

  7. #7
    Sencha Premium Member Iveco's Avatar
    Join Date
    Aug 2007
    Location
    Germany
    Posts
    70
    Vote Rating
    1
    Iveco is on a distinguished road

      0  

    Default


    Quote Originally Posted by Iveco View Post
    1) I attached a 'celldblclick' event to the grid, thought when the row gets expanded, the visible expanded area doesn't have this event, how can I also attach the celldblclick event to the rowExpander's area for that row?
    Got this fixed by using "rowdblclick".

    Quote Originally Posted by Iveco View Post
    2) When I have a row expanded and showing the rowExpander, meanwhile updating the grid's datastore (though refreshing the grid), it will show all rows collapsed, even if some of them before the update expanded but the ICON's doesn't get changed. I tried adding an event to the datastore, which then will check all rowExpander.state for that row and replaceClass the Icon but the problem is state only stores the id of the row not the rowIndex itself.
    Got this fixed by using:

    Code:
    Ext.select('div.x-grid3-row-expanded').replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
    (dont forget to reset the state's with the for loop I posted more top)

    Regards,

    Iveco

  8. #8
    Sencha User nak1's Avatar
    Join Date
    Jan 2008
    Posts
    266
    Vote Rating
    0
    nak1 is an unknown quantity at this point

      0  

    Default Thoughts

    Thoughts


    Hi CutterBl, I was wonder if you could give me a hand on a dilemma I'm having using RowExpander. I want to render components that I've executed multiple times. I know you can't render a component multiple times, but it seems like a waste to rebuild each time someone clicks the expander on my grid. Here's my forum post; http://extjs.com/forum/showthread.ph...ander+multiple

    Based on your post, I'm thought you'd know the answer. BTW I'm a CF dev guy myself.

    N
    No longer a Newbie

  9. #9
    Sencha Premium Member
    Join Date
    Feb 2008
    Location
    Canada
    Posts
    7
    Vote Rating
    0
    a274688 is on a distinguished road

      0  

    Smile Thanks for the great work

    Thanks for the great work


    Hi CutterBl

    Thanks for the great plugin ... works like a charm ... saved me hours of work.

  10. #10