Conditional Rendering in Asp.Net MVC Views

An entry about asp.net mvc Publication date 21. March 2008 12:19

One of the things that annoy me a bit about the Asp.Net MVC framework, is the way it causes a littering of yellow brackets in the markup for my view templates. Say for instance you're writing the template for a view that should display a list of customers, and for each customer you want to show an icon representing the status of that customer so that it is easy to distinguish between regular, preferred, affiliate customers and so forth. What you may end up with, is the following mayhem of yellow <% %> blocks:

<table id="customers_table" cellspacing="0">
    <thead>
        <tr>
            <th>Name</th>
            <th>Status</th>
            <th>Primary Contact</th>
            <th>Phone Number</th>
            <th>Email</th>
        </tr>
    </thead>
    <tbody>
 
    <%foreach (ICustomer customer in this.ViewData){%>    
        <tr>
            <td><%=customer.Name%></td>
            <td>
                <% if(customer.Type == CustomerType.Affiliate) %>
                    <%= this.Html.Image("~/Content/Images/customer_affiliate_icon.jpg")%>
                <% else if(customer.Type == CustomerType.Preferred) %>
                    <%= this.Html.Image("~/Content/Images/customer_preferred_icon.jpg")%>
                <% else %>
                    <%= this.Html.Image("~/Content/Images/customer_regular_icon.jpg")%>            
            </td>
            <td><%=customer.PrimaryContact.Name%></td>
            <td><%=customer.PrimaryContact.PhoneNumer%></td>
            <td><%=customer.PrimaryContact.Email%></td>
        </tr>
    <%}%>
    </tbody>
</table>

 

If you're not careful about the formatting of the markup, these sorts of conditional rendering blocks can quickly become fairly difficult to maintain.

Extension Method Addiction?

I'm starting to worry that I might be developing an extension method addiction, because lo and behold: I've come up with another one to this time extend the HtmlHelper API with a method which simplifies conditional rendering quite elegantly, using the fluent interface technique:

<%foreach (ICustomer customer in this.ViewData){%>    
    <tr>
        <td><%=customer.Name%></td>
        <td>
            <%= this.Html
                   .If(customer.Type == CustomerType.Affiliate, html => html.Image("~/Content/Images/customer_affiliate_icon.jpg"))
                   .ElseIf(customer.Type == CustomerType.Preferred, html => html.Image("~/Content/Images/customer_regular_icon.jpg"))
                   .Else(html => html.Image("~/Content/Images/customer_regular_icon.jpg")) %>
        </td>
        <td><%=customer.PrimaryContact.Name%></td>
        <td><%=customer.PrimaryContact.PhoneNumer%></td>
        <td><%=customer.PrimaryContact.Email%></td>
    </tr>
<%}%>

 

That looks a lot better, don't you agree? The use of delegates ensures that only the chosen conditional branch will actually be invoked, so there is no overhead to speak of. Here's the code for the extension method (I whipped this together in a few minutes, so there might very well be improvements that can be made to the implementation):

public static class HtmlHelperConditionalExtensions
{
    /// <summary>
    /// Begins a conditional rendering statement
    /// </summary>
    /// <param name="helper"></param>
    /// <param name="condition"></param>
    /// <param name="ifAction"></param>
    /// <returns></returns>
    public static ConditionalHtmlRender If(this HtmlHelper helper, bool condition, Func<HtmlHelper, string> ifAction)
    {
        return new ConditionalHtmlRender(helper, condition, ifAction);
    }
 
    public class ConditionalHtmlRender
    {
        private readonly HtmlHelper _helper;
        private readonly bool _ifCondition;
        private readonly Func<HtmlHelper, string> _ifAction;
        private readonly Dictionary<bool, Func<HtmlHelper, string>>  _elseActions = new Dictionary<bool, Func<HtmlHelper, string>>();
 
        public ConditionalHtmlRender(HtmlHelper helper, bool ifCondition, Func<HtmlHelper, string> ifAction)
        {
            _helper = helper;
            _ifCondition = ifCondition;
            _ifAction = ifAction;
        }
 
        /// <summary>
        /// Ends the conditional block with an else branch
        /// </summary>
        /// <param name="renderAction"></param>
        /// <returns></returns>
        public ConditionalHtmlRender Else(Func<HtmlHelper, string> renderAction)
        {
            return ElseIf(true, renderAction);
        }
 
        /// <summary>
        /// Adds an else if branch to the conditional block
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="renderAction"></param>
        /// <returns></returns>
        public ConditionalHtmlRender ElseIf(bool condition, Func<HtmlHelper, string> renderAction)
        {
            _elseActions.Add(condition, renderAction);
            return this;
        }
 
        public override string ToString()
        {
            if(_ifCondition) // if the IF condition is true, render IF action
            {
                return _ifAction.Invoke(_helper);
            }
 
            // find the first ELSE condition that is true, and render it
            foreach(KeyValuePair<bool, Func<HtmlHelper, string>> elseAction in _elseActions)
            {
                if(elseAction.Key)
                {
                    return elseAction.Value.Invoke(_helper);
                }
            }
 
            // no condition true; render nothing
            return String.Empty;
        }
    }
}

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

NDC 2010

The conference to attend this summer happens June 16th-18th in Oslo, Norway. Are you going? Be sure to catch my talk on AOP while you're there!

 

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS