Medryx Observations

July 29, 2008

A Dynamic Tab Container

Filed under: dojo — Tags: , , — maulin @ 4:37 pm

I think tab containers are great. And I think dojo’s tab container implementation is better than great. Its so easy with simple markup to make tabs and really create elegant user interfaces. Further, you lazy load the content of the tabs to make for a reall good user experience, all without much effort at all.

But I frequently use tabs as a tool to filter data views. For example, if I have a table, and want a few standard “views” of that table, I’ll put each view in a tab. Well this leads to a lot of repetitive code. What I really want is to tell the tab container what tabs I want to build, and then create a few small bits of data that can be plugged into each view to “customize” it.

So the “old way” of doing this looked like the following. I’ll use the “stateStore” from many of dijit’s tests as my sample data.

<div>
<div title="All">
<h2>All</h2>
<table border="0">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
</table>
</div>
<div title="Countries">
<h2>Countries</h2>
<table border="0">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
</table>
</div>
<div title="Cities">
<h2>Cities</h2>
<table border="0">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
</table>
</div>
</div>

That’s a lot of code to have three tabs each with a slightly different filtered view of the same table. Enter DataTabContainer.

<div>
<h2>${name}</h2>
<table border="0">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
</table>
</div>

Ahhh.. Much better. Well, once I had this working for a simple array of tabs, I thought — “how about I swap in a store for the tabs?”. So instead of (or in addition to) the “data” attribute of the widget, you can provide a “store”, “query”, “queryOptions”, and “sort” attribute, much like DataGrid. If you have both a store and data, the tabs in “data” are pre-pended to the store objects. This allows you to include an “All” tab quite easily at the front of the container.

This was actually quite a bit trickier to build than I initially thought. I ended up using a lot of code from dojox.widgets.Iterator, munged with the store/query code from DataGrid to put it together. It basically works like this:

  1. Convert the child elements of the DataTabContainer to a custom widget via dijit.Declaration (DataTabContainer, it may surprise you, actually extends dijit.Declaration, and not TabContainer).
  2. In the buildRendering stage, copy down to the location of the node (since dijit.Declaration is a “pseudo” widget that destroys its own domNode after parsing it).
  3. In place of the destroyed node, create a TabContainer.
  4. Iterate over the data array (or fetch the store items) and create tab for each item in the array. (Using the “titleAttribute” attribute of DataTabContainer as the title of each tab).
  5. In startup(), create an instance of the dijit.Declaration class for the currently selected tab. Before doing so, do a string substitution on the dijit.Declaration class’s prototype.templateString. Note that this is not a simple dojo.string.substitute because it uses the store’s getValue method to get the value to place in the substitution.

And that is it. It works pretty well. Other features I could imagine:

  • If the DataTabContainer is the child of an already existing TabContainer, then use an attribute like “embed=’true’” to skip the creation of the TabContainer step in buildRendering, and instead, just add the tabs to the surrounding container.
  • Allow a “pre-data” and “post-data” to add items around the store fetch items. Right now it only supports “pre” data.

Let me know what you think!


Update:

Well you can forget almost everything I mention about the implementation of this above. I have figured out a much simpler implementation that creates a mixin class to TabContainer, AccordionContainer, or StackContainer and uses the content of the container as a “templateString” that is sent to ContentPane.setContent. Much easier. And now you can do dynamic Accordions and Stacks. Nice! So the updated source is here. And you can try it here. Just remember, to use this code you need to dojo.require the original container (TabContainer, AccordionContainer, or StackContainer), the original pane (ContentPane), and the mixed in stuff (medryx.widgets.DynamicStackContainer).

2 Comments »

  1. What happened to the code? :(

    Comment by Eric Clemmons — September 30, 2008 @ 11:55 pm

  2. I opened a ticket on trac.dojotoolkit, and its been put off. You can snag it on my dev machine at:

    http://dev.medryx.org/lib/js/dojo-extensions/dojox/widget/DynamicStackContainer.js

    with a working test at

    http://dev.medryx.org/lib/js/dojo-extensions/dojox/widget/tests/test_DynamicStackContainer.html

    Keep in mind this is using a dev version of dojo 1.2, so it slow to load. it is MUCH faster after it is built with dojo build tools.

    Comment by maulin — October 3, 2008 @ 12:49 am

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress