I think it is a safe assumption that every web developer has had to display tabular data at one point or another. Tabular data is easy with ASP.NET: bind a GridView to a data source, and you’re all set. But with ASP.NET MVC, things are a little trickier. We don’t have access to all the nice WebForms controls. Still, it’s fairly easy to do: just write a for-loop, or better yet, use the grid helper from MvcContrib.
Things get a trickier though if your tabular data is also hierarchical. Typically, we display hierarchical data in a tree of some kind, but trees really aren’t great for tabular data. What would be great is to combine the two somehow. Fortunately, there’s a nice plug-in for jQuery that does just that: ActsAsTreeTable. It’s easy enough to use; all you have to do is embed ID’s and CSS class information in your table rows, and the JavaScript does everything else. Here’s a simple example from the docs:
1: <link href="path/to/jquery.acts_as_tree_table.css" rel="stylesheet" type="text/css" />
2: <script type="text/javascript" src="path/to/src/jquery.acts_as_tree_table.js"></script>1:
2: <script type="text/javascript">3:
4: $(document).ready(function() {5: $("#your_table_id").acts_as_tree_table();6: });
</script>
3:
4: ...
5:
6: <table id="tree">7: <tr id="node-1">8: <td>Parent</td>9: </tr>10: <tr id="node-2" class="child-of-node-1">11: <td>Child</td>12: </tr>13: </table>
We can now combine this with the grid from MvcContrib to produce a collapsable Grid Tree View. This example is encapsulated inside a view user control so that it can be used on any page. It displays imaginary "widgets" in a tree. The widgets aren’t really hierarchical, so I’ve fudged it by making it appear that each widget is a child of its predecessor in the table.
First, let’s create the grid:
1: <%1: Html.Grid(GetWidgets(), new Hash(id => ClientID, style => "width:100%"),2: column =>
3: {
4: column.For(w => w.Name);
5: column.For(w => w.Description);
6: column.For(w => Html.TextBox("Description_" + w.Id, c.Description), "Editable").DoNotEncode();7: },
8: sections =>
9: {
10: sections.RowStart(c =>
11: {
%> <tr class="child-of-node-<%=w.Id - 1%>" id="node-<%=w.Id%>"> <%
1:
2: });
3: } );
%>
That probably looks horrendous, so let’s walk through it. GetWidgets() is a method on the view user control that grabs widgets from wherever (in practice, probably the model or view data). Next, the Hash just contains key/value pairs that are embedded in the opening table tag; here, we’ve specified the table’s ID (by using ClientID, it will have the name that ASP.NET gives to the user control), and we’ve specified that it should be 100% wide. Next, we define the columns using lambda expressions. The first two columns simply display the widget’s name and description. The last column is a little more complicated. It creates a text box using the TextBox helper method. Since the column contains HTML that shouldn’t be encoded, we call DoNotEncode on it. Finally, we use a lambda expression to override how rows are created. The code here populates the row with the ‘child-of-node-#’ class and the id attribute, both of which are needed by ActsAsTreeTable. It may look intimidating, but it’s actually nice once get comfortable with the syntax.
The last thing we need to do is spit out the JavaScript to turn our gird into an ActsAsTreeTable:
1: <script type="text/javascript">
2: $(document).ready(function() {
3: $("#<%=ClientID %>").acts_as_tree_table();
4: });
5: </script>
If you set everything up correctly, you should now have a working "grid tree view". In a future article, I’ll introduce a new Html helper that does all the heavy lifting for you.