Saturday, September 27, 2008

Ext JS Custom TreeNodeUI

I wrote this some time ago and then noticed that Blogger blasted the indentation in the code snippets so I'm only posting it now.

Hey, this should be a good dump - I needed an Ext JS tree with a custom TreeNodeUI. In addition to the usual text, I wanted each TreeNode to feature a select box that would allow a selection (and Ajax saving of the selection) to be made for each node. While the Ext JS API Documentation is pretty good, it doesn't always provide the information needed when trying to "step outside the box." (This framework is just dying for someone to write a book about it...) After just a little head scratching I found writing such a custom TreeNodeUI to be fairly straight forward.

The first thing I needed to do was to define my SelectNodeUI having it call its parent constructor on instantiation:

After that, I needed to extend my SelectNodeUI, inheriting from the standard TreeNodeUI, while overriding the renderElements and onSelectChange methods. The renderElements method is overridden to add the select box (look for the sel variable). The onSelectChange method is overriden to prevent the normal focus change which would unfortunately cause the select box to close immediately when first selected. This looks like a lot of code but is primarily a copy of the TreeNodeUI renderElements method with the rendering of the select box added. I've marked the parts I've added / changed with // DJB: comments:

Take special note of the building of the selectHtml string from the COMMAND_HIERARCHY_TYPE_ARRAY which has been written into the page in the format of [["label text", id], ...] and how the correct option is selected after the selectHTML has been rendered into a select box DOM node.

Now that the custom tree node is defined and ready to use the loader for the TreePanel needs to be informed about which UI providers are available. This is done by setting the uiProviders property of the loader property of the TreePanel:

With this code in place, a mapping has been defined between the string "select" and the SelectNodeUI. If the JSON for a tree node comes back with the value uiProvider: "select", the SelectNodeUI will be used to render that tree node, otherwise, it will fall back to using a regular TreeNodeUI. Keep in mind that such a tree node will also need a typeId property so that the proper option can be selected in the select box. With Rails the JSON for these tree nodes might be rendered with a method like this:

Unfortunately, as the comment says in the code, Internet Exploder does not bubble change events. Thus each select node has its own change listener that calls selectChange (otherwise I could have wired up a single change listener at a level above these tree nodes in the DOM tree). selectChange is a fairly straight forward function which uses an Ajax call to save the new state for the tree node's underlying model:

Finally, the select boxes are slightly taller than the normal content held at a tree node and thus mess up the lines that are drawn connecting the + / - expanders along the left side of the tree. Instead of fixing this up to look write I would recommend just turning the tree lines off. This can be done with by setting up the TreePanel with either the lines: false or useArrows: true properties. With lines: false the tree will render with the same old + / - expanders but won't render the lines. With useArrows: true the tree will render with a look that is supposed to emulate "Vista," using small triangles and drawing no lines. I'm not sure what this "Vista" thing is and I couldn't seem sudo apt-get install it ;-).