ExtJs components in GridPanel columns

My first blog of the new year.. Wishing all my readers a very Happy and Prosperous New Year 2011....

Coming to the topic, I have needed to have ExtJs components in GridPanel columns many times. I have been using ExtJs components in GridPanels for sometime now, but thought of writing a blog post for it only after I saw a couple of people having trouble doing so.

Really it was more easy that I anticipated it to be, the first time I had an ExtJs component (a Button) in a GridPanel column. All that is needed is to specify a renderer for your column, create the ExtJs component in your renderer method, and return the html for the component.

You can see an example of the same below. You would notice that the third column in the GridPanel has 3 different types of components, Buttons, ProgressBars, and TextFields. You can virtually have any ExtJs component you need in the GridPanel.

 

Only these few lines of code were required for having the components in the GridPanel column above:

 

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }function extjsRenderer(value) { var id = Ext.id(); (function() { if (value < 50) { var bar = new Ext.ProgressBar({ height: 15, renderTo: id, value: (value / 100) }); } else if (value < 75) { var btn = new Ext.Button({ renderTo: id, text: 'Price: ' + value }); } else { var txt = new Ext.form.TextField({ value: value, renderTo: id, height: 15 }); } }).defer(25); return (String.format('<div id="{0}"></div>', id)); }{/syntaxhighlighter}

 

You would notice that the code generates a unique id (using Ext.id) and returns a div with its id set to the unique id created. It also generates the desired ExtJs component with its renderTo config option set to the id that was created. An important point to note is that the component is created after a delay. Let me explain why.

My original approach created a couple of nested divs, then instantized the desired ExtJs component specifying the inner div as the rendering target for the ExtJs component (renderTo: innerdiv) and finally returned the html for the inner div (including the tags for the inner div).

The code worked, but there was a gotcha. Some components did not render perfectly with this approach (e.g. ProgressBar) and I had to write additional code for ensuring things work. My guess is it happened because the ExtJs component was rendered to a DOM Element created via a call to document.createElement. But later, I simply returned the html for this element, and ExtJs created a new DOM element from the returned html. I used afterrender listeners to correct any issues due to this.

Then as pointed out by Eugene in the comment below, using an id and deferred rendering helped resolve any issues, and I updated the above code accordingly. In this approach, we create and return a div with a known unique id from the renderer method. Then after a delay, which ensures that ExtJs would have added the returned div to the DOM, we instantize the desired ExtJs component and use the known unique id as the rendering target (renderTo: id) for the ExtJs component. This renders the component at its desired place inside the GridPanel column, and the component behaves normally without any issues.

I am going to file a feature request to the ExtJs team requesting them to allow returning an Html/ExtJs element from column renderer methods also, in addition to the html that is now required to be returned.

The complete code for the above example is attached below.

 

AttachmentSize
HTML icon ExtJs-Components-in-GridPanel.htm4.99 KB

Comments

Nice post, but seems I know more accurate solution:

{ header: "...", dataIndex: "...", width: 200,

  renderer: function(value, meta, record) {

    var id = Ext.id();

    (function() { new Ext.ProgressBar({ renderTo: id, value: value, }); }).defer(25);
    return '<span id="' + id + '"></span>';

  } }

afterrender event handling needed only for classes adding. Proper dimensions will be calculated automatically.

rahul's picture

Hi Eugene, thanks for sharing this. I was thinking that adding an id might help, but never got time to test it. Let me test and update the code.

Only one issue remains unsolved for me - font size after progress bar height changing. In your example row looks a bit stretched and you can safely decrease progress bar height to avoid this. But in case when progress bar has text attribute specified you will see ugly unscaled font after height changing.

rahul's picture

Hi Eugene, thanks for pointing this out. I am currently short on time to test this, but I believe there would be very rare cases where one would have a need to change ProgressBar height after it has been rendered to the GridPanel column.

Having said that, if there is really such a requirement, I believe you should be able to override some ProgressBar method and scale the font manually with your row's height (these work-arounds are to be expected as ExtJs itself does not support rendering Components to GridPanel columns).

One thing you might want to try is to increase your defer delay, try increasing it to something like 1 second or more and test again. If it makes a difference, you can try reducing the delay to a threshhold level, where the issue does not arise. If this does not make a difference, then I think overriding ProgressBar methods is the only alternative.

Hi Rahul, I think problem is not related to grid/deferring time. I found some related info in Ext JS forums: Ext.ProgressBar size height, FAQ entry. Seems this is well known progress bar problem and solution from Animal only partially works in my case (default theme/fonts configuration, font scales better but looks ugly and has wrong margins anyway).

But you are right - problem can be solved with overriding (subclassing even better in this case) onRender method of progress bar. Not generic but workable solution.

Sorry for taking your time - just want to share some experience. Your post on third place in google for "ext js gridpanel progressbar" query ;)

rahul's picture

Hi Eugene, thanks again for sharing the solution to the issue. And I just said, I might not be able to test your problem with ProgressBar heights, there's nothing to feel sorry, and I in my capacity try to respond to comments on my blog posts...

And thanks for sharing your experience. Your first comment presented a better way of having components in the GridPanel, and I am sure other readers would find the updated code more useful :)

Is there any other solution other than use of defer method?

rahul's picture

Well, I currently cannot imagine another way...

Hi Rahul

ur example was of very handy to accomplish my requirement. There is a small clarification for which i think u may have an answer.

i have rendered textbox / password field based on a parameter on a gridpanel. It works perfect as per ur suggestion. Now when i submit i could not retrieve the value of the textbox/password  filed. if any one could throw some light , it would be highly helpful.

Thanks in advance
Anbarasan

rahul's picture

Hi Anbrasan, you would need to maintain the reference for the rendered component somewhere (I might have created an array and then added references for the component in each row to this array).

Then before submitting, I would loop over this array and retrive the values using getValue() from the components and add them to whatever data is being submitted (probably in the GridPanel Store, but depends upon your requirement). This would need to be done manually and ExtJs would not help you with this.

hi rahul pls send me the code that how can i get the values from the grid column component 

first i am binding the values to the component from the store but when i change the value i am not able to get  the new values from the component

this is my renderer code

renderer: function (v, m, r) {

                     var ids = Ext.id(); alert(ids);

                     Ext.defer(function () {

 

                         var txt = new Ext.form.TextField({value:v,

                             id: 'txt_DC', name: 'txt_DC',

                             renderTo: ids

                         });

                         b = Ext.getCmp('txt_DC').getValue();

                         //r.set('C') = b;

                         c = c + b + ',';

                     }, 50);

                     return Ext.String.format('<div id="{0}"></div>', ids);

                 }

i am able to get only the values which are binded from the store

so pls send the code how to do it 

Thank in Advance.

I like it very much..
Thank you

I have a panel in which i am putting a grid panel.

set property autoscroll:true

I get 4 values from store. i populate the grid with single column using renderer for formating. I am using similar method stated above.

The problem I am facing is, If the height of grid grows beyond panel, scroll bars are is shown.

I thing What is happening is that first panel is loaded with empty div so no scrollbars for panel, but after 200 ms i fill the rows with html / component using renderer and height increases, but panel was already painted so it doesnot gives scroll bar.

Any workaround for this??

Sorry Made a mistake  I Mean To Say

The problem I am facing is, If the height of grid grows beyond panel, scroll bar are is not shown. so some rows are not shown..

Please help

rahul's picture

Hi Sagar, please post a link where I can see your code working live to be able to help. You can do it at jsFiddle.net

Great! You just save me

I'm not going to lie, I think think this was possible without major hacks on the code.

Very nice, sir.

Hi Rahul,

This works great. The only catch I see, is that if you update the row via store, the values of rendered textfields using your approach are resseted. Also happens after sorting the column. 

Any idea how to solve this problem ?

rahul's picture

Hi Rob, I guess you would need to hook into keyup or blur event of the textfield in each row and update the underlying record's value with textfield's value. This way any refresh of the view would lead to rendering of new textfields with updated values.

thanks for your code!

Hi All,

I have an Editable grid panel with multiple rows,I need to have fileupload/Mutiple fileupload for one of the column in the grid to attach the different files for each row. After selecting the files in each cell they uploaded and reference file names should be saved in the grid hidden field before submitting the editable grid

Is there any solution to upload to files and keeping the uploaded files in hidden field before submitting the entire grid

Thanks in advance

Srinivas V

rahul's picture

Hi Sri, you should be able to use approach highlighted in the following blog post:
http://www.rahulsingla.com/blog/2011/04/html-using-buttons-for-selecting-files-to-be-uploaded

You can use a Button column to trigger file selection, and another template column to house input file fields. Once a file is selected, move it to a dynamically created file field inside the template column (see the above referenced blog post for hints on how to do this).

BTW, creating per row file fields isn't going to be much of a use and would be tricky to implement. You might well append the input file fields to the form enclosing your GridPanel and use a naming scheme for field's "name" parameter to recognize which grid row a input field belongs to.

Thank you Rahul

Hi Rahul,

I was looking for column based serach in a grid. The requirement is such that a search text field will appear below the column name in all the columns of the grid. That text field will should search once the user hits the enter button, but only in that particular column.

I am trying to find this from many days. But I am not getting it, As I am new to ExtJs I dont know weather it is possible or not. So please suggest me some solution.

Thanks & Regards

Harsha

rahul's picture

Hi Harsha, adding search text fields below column headers is easy (see this for example). However if you need to have different number of rows per column, then GridPanel is not the component for that.

However you can very easily achieve a similar effect by using multiple Panels in a hbox layout and putting a search field and a DataView in a vbox layout in each Panel (alternatively use single column GridPanels in hbox layout each GridPanel using GridHeaderFilter plugin for the single column).

Hi,

Thanks Rahul.

Looking at the example you suggested I have got some useful info regarding that.

I have googled few similar examples using your eample. I have got one implementation which suits my requirement. Check this http://developerextensions.com/index.php/extjs-4-filter-row-demo

As my grid is in Xml. Can I add an text box below the column header in the Xml, or do i need to add in the JavaScript??

I dont want all the functionalities of Grid filtering. I just want to search by entering in the text box for respective columns. Is It possible to do that in Xml and Js??

Thanks & Regards,
Harsha

rahul's picture

Hi Harsha, I already suggested my solution in previous comments (multiple panels or single column gridpanels in hbox layout). The example you referenced filters all columns (not the one where criteris was selected).

HI 

 

Can u please tell me that how to Changing-empty-text-of-a-grid-when-store-is-already-empty.

 

 

Thanks

Lokesh

Hi, very nice post, plz make tutorial on extjs 4.1. I try that but that don't working :(

Hey Rahul,

I am facing problem while same grid i am building in extjs 4.2 and main problem is when i m clicking on grid containing TextField,TextAres and Combobox then i am not able to enter values . i meant its loosing focus ,yes if i click two times then component is allowing me to enter Values,its stoping my delieverable work Please help me ASAP. i am looking forward for your response ,

Thanks in advance