9

ASP.NET and JQuery MVC Menu Part 1

Other posts in this series:

One of the key differences between ASP.NET Web forms and ASP.NET MVC is that WebForms contained many controls which you could use on your web pages and MVC doesn’t.

Not that this is a bad thing, the difficult thing about WebForms was the horrendous html they generated and the hoops you had to go through to gain fine control over them.

In this series of posts I will describe how to create a fully functional Menu using Html, JQuery and some C# Helper Methods.

Part 1 – Creating an MVC Menu

Creating a menu in MVC is actually pretty straightforward once you realise you’re going to have to do some of the dirty work yourself.

Start with a standard Unordered List.

  1. <ul id="main-menu">
  2.     <li>
  3.         <a href="/">Home</a>
  4.     </li>
  5.     <li>
  6.         <a href="/ContactUs">Contact Us</a>
  7.         <ul>
  8.             <li>
  9.                 <a href="/FindUs">How to find us</a>
  10.             </li>
  11.         </ul>
  12.     </li>
  13. </ul>

Note that we have another unordered list inside the main list to create a hierarchical menu.

Now I know what you’re thinking, that’s not MVC that’s just HTML. So let’s make life easier for ourselves and introduce a helper method to render these list items for us.

  1. public static string ListItem(this HtmlHelper helper, string linkText, string actionName, string controllerName)
  2. {
  3.     TagBuilder builder = getListItem(helper, linkText, actionName, controllerName);
  4.     return builder.ToString();        
  5. }

Nothing fancy here, but note that we’re passing in the action and controller names. The actual work is done in the getListItem method.

  1. static TagBuilder GetListItem(HtmlHelper helper, string linkText, string actionName, string controllerName)
  2. {
  3.     var linkHtml = HtmlHelper.GenerateLink(helper.ViewContext.RequestContext, helper.RouteCollection, linkText, null, actionName, controllerName, null, null);
  4.  
  5.     var builder = new TagBuilder("li");
  6.  
  7.     var isCurrent = IsCurrentAction(helper, actionName, controllerName);
  8.  
  9.     if (isCurrent)
  10.     {
  11.         builder.MergeAttribute("class", "current");
  12.     }
  13.  
  14.     builder.InnerHtml = linkHtml;
  15.     return builder;
  16. }

This is where the fun starts (honestly!).

  1. First off we generate a link for the specified action and controller. This will work with whatever routing you have set up and generate a link to the action.
  2. We create a new TagBuilder to render our list item
  3. We’ll look at the IsCurrentAction method next, it checks if the specified action/controller is the one currently being presented to the user
  4. If this is the current Action then set the list item’s class to “current”
  5. Put the link inside the li tag

IsCurrentAction is pretty straightforward too…

  1. static bool IsCurrentAction(HtmlHelper helper, string actionName, string controllerName)
  2.         {
  3.             string currentControllerName = (string) helper.ViewContext.RouteData.Values["controller"];
  4.             string currentActionName = (string) helper.ViewContext.RouteData.Values["action"];
  5.  
  6.             if (currentControllerName.Equals(controllerName, StringComparison.CurrentCultureIgnoreCase) &&
  7.                 currentActionName.Equals(actionName, StringComparison.CurrentCultureIgnoreCase))
  8.             {
  9.                 return true;
  10.             }
  11.  
  12.             return false;
  13.         }

So how would you use your new helper methods?

  1. <ul id="main-menu" class="menu collapsible">
  2.     <%= Html.ListItem("Home", "Index", "Home") %>
  3.     <%= Html.ListItem("Contact Us", "Index", "ContactUs") %>
  4. </ul>

This will render the list items and put a class=”current” attribute on the one for the page currently being viewed.

Now an obvious problem with this approach is that you can’t embed sub menus (ULs within LIs). The way around this is to mimic the MVC framework itself and introduce new BeginListItem and EndListItem methods (ASP.NET MVC has BeginForm and EndForm).

  1. public static string BeginListItem(this HtmlHelper helper, string linkText, string actionName,string controllerName)
  2. {
  3.     string result;
  4.  
  5.     TagBuilder builder = GetListItem(helper, linkText, actionName, controllerName);
  6.     result = builder.ToString(TagRenderMode.StartTag);
  7.     result += builder.InnerHtml;
  8.  
  9.     return result;
  10. }

This opens the li tag. The EndListItem method closes it.

  1. public static void EndListItem(this HtmlHelper helper)
  2.         {
  3.             HttpResponseBase httpResponse = helper.ViewContext.HttpContext.Response;
  4.             httpResponse.Write("</li>");
  5.         }

And we can use these methods as follows.

  1. <%= Html.BeginListItem("Contact Us", "Index", "ContactUs")%>
  2.     <ul>
  3.         <%= Html.ListItem("Find Us", "Index", "FindUs")%>                   
  4.     </ul>
  5. <% Html.EndListItem(); %>

Next Time

Up next is how to spice this menu up and make it all “interactivy” using JQuery,

  • Anonymous

    Great post. Is the Jquery part coming soon.

    Thanks

  • MrTea

    Part two is now up (I know I know it took far too long to get round to it!)

    Read it here…

    http://telldontask.wordpress.com/2009/10/05/asp-net-and-jquery-mvc-menu-part-2/

  • nice tuto in here , good introduction

    hear from you soon thanks

  • Thanks for the topic. Here’s some code to use it extending the AjaxHelper using your logic. This would also require some javascript functions (which I’ve not included)

    namespace System.Web.Mvc.Ajax {
    public static TagBuilder GetNavItem(this AjaxHelper ajax, MolineSoftware.SLAudit.Navigation navItem) {

    var htmlTemplate = ajax.ActionLink(navItem.LinkText, navItem.ActionName, navItem.Controller, new AjaxOptions {
    UpdateTargetId = navItem.UpdateTargetId,
    OnSuccess = navItem.OnSuccess,
    OnBegin = navItem.OnBegin
    });
    var builder = new TagBuilder(“li”);

    if (IsCurrentAction(ajax, navItem.ActionName, navItem.Controller))
    builder.MergeAttribute(“class”, “current”);

    builder.InnerHtml = htmlTemplate.ToHtmlString();
    return builder;
    }

    static bool IsCurrentAction(AjaxHelper ajax, string actionName, string controllerName) {
    string currentControllerName = (string)ajax.ViewContext.RouteData.Values[“controller”];
    string currentActionName = (string)ajax.ViewContext.RouteData.Values[“action”];

    if (currentControllerName.Equals(controllerName, StringComparison.CurrentCultureIgnoreCase) &&
    currentActionName.Equals(actionName, StringComparison.CurrentCultureIgnoreCase)) {
    return true;
    }
    return false;
    }
    }
    public class Navigation {

    #region | Public Properties |
    ///
    /// Gets or Sets the ID
    ///
    public virtual Int32 Id { get; set; }

    ///
    /// Gets or Sets the Display for the link
    ///
    public virtual String LinkText { get; set; }

    ///
    /// Gets or Sets the ActionName.
    ///
    public virtual String ActionName { get; set; }

    ///
    /// Gets or Sets the Controller
    ///
    public virtual String Controller { get; set; }

    ///
    /// Gets or sets the Active flag.
    ///
    public virtual Boolean Active { get; set; }

    ///
    /// Gets or sets the JavaScript function to call after the page is successfully updated.
    ///
    public virtual String OnSuccess { get; set; }

    ///
    /// Gets or sets the name of the JavaScript function to call immediately before the page is updated.
    ///
    public virtual String OnBegin { get; set; }

    ///
    /// Gets or sets the ID of the DOM element to update by using the response from the server.
    ///
    public virtual String UpdateTargetId { get; set; }
    #endregion

    }
    public class NavigationList : List {

    #region | Constructor |
    public NavigationList()
    : base() {

    }
    #endregion

    }

    In your controller you’d load your nav items.
    NavigationList list = new NavigationList().GetTestList(); //GetTestList is your List items
    ViewData[“nav”] = list;

    In your view:

  • Looks like it cut off the view code:

  • Pingback: ASP.NET and JQuery MVC Menu Part 2 « Tell don't ask()

  • Sean

    Is there a download source code for this?

  • Pingback: Dynamic Menu in Asp.net MVC2 from Sitemap | Jisku.com - Developers Network()

  • Pingback: ASP.NET and JQuery MVC Menu Part 3 (AJAX) | Tell don't ask()