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.