Hierarchical Data: Rendering with Razor

I’ve walked through persisting hierarchical data via closure tables and then through abstracting the closure table in the application layer.  Now, we’ll look at rendering our hierarchy in an ASP.NET MVC 3 sample application using Razor. For the styling of the hierarchy I use jquery treeview which I quite like for its simplicity.  The typical approach to drawing tree views / hierarchies in the view is nested html unordered lists (<ul>).  To traverse and render our Hierarchy of family members we’ll leverage the power of the Razor view engine to create a set of reusable partial views for drawing our hierarchy whenever we need it.

When its all said and done, we’ll end up with:

Well, that looks simple enough, let’s see how we get there!

Hierarchy.cshtml

@model Hierarchy
@{
    ViewBag.Title = "Hierarchy";

    //Create the PartModel for the Hierarchy in preparation for the ViewPart
    //Note we are passing in the name of the ViewPart we want to use
    //This allows the rendering of the node to be defined inependently
    //of the structural rendering of the hierarchy
     var partModel = new HierarchyPartModel()
                    {
                        Hierarchy = Model,
                        HierarchyNodeViewPart = "Parts.Home.FamilyMemberNode"
                    };
  }

@if (Model != null)
{
    //Call upon our Hierarchy ViewPart to handle the rendering
    //of the hierarchy structure, passing in our HierarchyPartModel
    //What we are accomplishing here is delegating out the logic
    //for rendering parts of the view to partial views. Hence, "ViewPart"
    @Html.Partial("Parts.Common.Hierarchy", partModel)
}
else
{
    @Html.RouteLink("Create Hierarchy", "AddNode", new { parentID = 0 })
}

Let’s take a moment to review what we just witnessed. First, it may be a little puzzling to find no trace of HTML in our view – what gives? Just one of the awesome aspects of Razor is the ease with which we can create layered views. Huh, sounds abstract, what does that -really- mean? Well, let’s reflect upon the more typical way of working with our view pages. Our controller would return a View (ActionResult) and when the view renders the HTML would exist entirely in the View at best some Editor/DisplayFor templates and some partial views (from Html.RenderPartial). What Razor allows us to do is grow beyond those more primitive template techniques and provide multiple Layouts and new ways to invoke partial views (Html.Partial), etc. My colleague and I began calling these partial views “ViewParts” and the View Models we pass in, “PartModels”. This approach was inspired by the work the Orchard team did with their approach using Razor. The benefit to this approach is a high degree of reusable HTML “controls/components” (ViewParts) and a clean distribution of view logic across the presentation layer. I admit, its awkward at first but after coding with it, I can’t think about it any other way.

Let’s peel back the layers now and see what’s going on in the ViewParts, starting with the PartModel we passed in.

HierarchyPartModel.cs

    //This is a logical equivalent to a ViewModel, except
    //specifically meant to support a ViewPart(s). The reason
    //for a derivative naming convention is to prevent confusion
    //with patterns that embrace ViewModels like MVVM
    public class HierarchyPartModel
    {
        //The core model we are working with
        public Hierarchy Hierarchy { get; set; }

        //This is the name of the ViewPart to use for rendering
        //each node of the hierarchy, we will see how this is used
        //in the ViewPart this PartModel supports
        public String HierarchyNodeViewPart { get; set; }
    }

Parts.Common.Hierarchy.cshtml

@model HierarchyPartModel

@*
    //Another very cool feature Razor provides us, inline
    //HTML helpers. The primary responsibility of this helper
    //is to assist with drawing the nested unordered list
    //for our hierarchy
*@
@helper DisplayHierarchy(FamilyMemberNode startingNode)
{
    <ul>
        <li>
            @*
                //Here we are rendering another ViewPart, specifically
                //the name of the ViewPart defined in our PartModel
                //The idea here is that we are separating the responsibility
                //of rendering the node from the structure itself
            *@
            @Html.Partial(Model.HierarchyNodeViewPart, startingNode)

        @*
            //If our node has children begin drawing those branches of the hierarchy
        *@
        @if (startingNode.HasChildren())
        {
            <ul>
                @foreach (var child in startingNode.Children)
                {
                    @*
                        //RECURSIVE CASE:
                        //Here we begin our logic all over again with
                        //drawing the node and nesting unordered lists
                    *@
                    @DisplayHierarchy((FamilyMemberNode)child)
                }
            </ul>
        }
        </li>
    </ul>
}

@*
    //Define our container for which jquery treeview will target
*@
<div id="tree">
    @*
        //Invoke the initial call to our helper
        //passing in the top-most root of our hierarchy
    *@
    @DisplayHierarchy(Model.Hierarchy.GetRoot())
</div>

Parts.Home.FamilyMemberNode.cshtml

@model FamilyMemberNode
@*
    //HTML definition for each node in the hierarchy
*@          
<div class="hierarchyNode">
    <div>@Model.FamilyMember.Name</div>
    @Html.ActionLink("Add", "Add", "Home", new { parentID = Model.FamilyMember.FamilyMemberID }, null)
</div>

So, quick recap. Our controller returns an ActionResult and when Hierarchy.cshtml is rendered it defines a HierarchyPartView shoving in the Hierarchy data structure and the name of the ViewPart to use as the HTML definition of each node. Then, Hierarchy.cshtml calls upon Parts.Common.Hierarchy.cshtml to handle rendering the structure of the hierarchy and it does so by leveraging a recursive inline html helper. From here, the ViewPart defined in the HierarchyPartModel is used to to render each node.

I wouldn’t be offended if at first you thought to yourself… “Hey buddy – way to over-engineer the hell out of your view, now I’ve got to inherit this onion”. On the surface, it may very well appear that way. However, let’s look at what we have gained with this implementation approach. We’re able to render a hierarchy anywhere we wish. I don’t argue this wasn’t possible in MVC 2 or even WebForms, but I do argue that Razor has made this approach FAR more manageable than prior view tech. Also, while I was implementing this approach on a project it was not decided which technology to use for styling the tree-view. This approach was validated when asked to change the various aspects of the tree. It proved to be very painless to quickly swap out the JQuery treeview approach for Telerik MVC Treeview. My nodes didn’t change, and neither did my Hierarchy.cshtml page. Also, the nodes needed to be defined differently depending on where the hierarchy was rendered in the view (and an array of other “business rules”). No problem, create a new ViewPart for defining the node and pass it in. Gone are the days of copy/pasting/fitting HTML across many views and feeling the burn of managing change in the presentation layer.

I’ve also used the technique with jstreetelerik mvc treeview, and even some cool open source css like Slickmap.  Hey, B^2 – what’s the point?  Right, the point is we’ve done a great job persisting and generating our hierarchy and we have total freedom in the UI to display our hierarchy with a high degree of reuse and flexibility.

Hopefully this series of working with Hierarchical Data has been useful – I know I learned a lot during discovery and implementation phases of these concepts.

Feel free to download and use any of the code from this sample project: DaHierarchy

6 Responses to “Hierarchical Data: Rendering with Razor”

  1. Actually have to write a similar coding expression this weekend. I will keep this in mind and try to use this template, it seems to simplify things. Will follow up with a response, but very ingenious. Good stuff!!!

  2. Thank you!
    Nice solution, to a tricky problem.

  3. That was easy –changed the connection string and voila, up and running. Now all you need to do is show us how to display both parents (I’m not a protozoa!), list the count of children when jsTree collapsed, and add a search to pick a node. Otherwise very cool, very helpful. Mucho Danke.

  4. cannot download the code

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: