Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.Net MVC help with refactoring

I'm quite new to both ASP.Net and MVC.

I got the following code in my master page:

  <div id="main-menu" class="menu">
  <%
   var items = (IList<CompanyName.Framework.Web.MenuItem>)ViewData["MainMenu"];
   if (items.Count > 0)
   {
    %><ul><%
    foreach (var item in items)
    {
     if (!string.IsNullOrEmpty(item.RequiredRole) && !System.Threading.Thread.CurrentPrincipal.IsInRole(item.RequiredRole))
      continue;

     %><li><a href="<%= item.Uri %>"><%= item.Title %></a></li><%
    }
    %></ul><%
   }
  %>
 </div>

Can I move the code to another file or refactor the code in any way?

edit:

My ApplicationController that all controllers derive:

public class ApplicationController : Controller
{
    List<MenuItem> _mainMenu = new List<MenuItem>();
    List<MenuItem> _contextMenu = new List<MenuItem>();

    protected IList<MenuItem> MainMenu
    {
        get { return _mainMenu; }
    }

    protected IList<MenuItem> ContextMenu
    {
        get { return _contextMenu; }
    }

    protected string PageTitle { get; set; }

    protected override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        ViewData["PageTitle"] = PageTitle;
        ViewData["MainMenu"] = MainMenu;
        ViewData["ContextMenu"] = ContextMenu;
        base.OnResultExecuting(filterContext);
    }
}
like image 276
jgauffin Avatar asked Dec 16 '22 20:12

jgauffin


1 Answers

Here are a couple of suggestions:

Improvement number 1: use view models and strongly typed views instead of ViewData

public ActionResult Index()
{
    // TODO: Fetch this data from a repository
    var menus = new[] {
        new MenuItem(), new MenuItem()
    }.ToList();

    return View(menus);
}

and then in your view:

<div id="main-menu" class="menu">
    <%
        if (Model.Count > 0)
        {
            %><ul><%
            foreach (var item in Model)
            {
                if (!string.IsNullOrEmpty(item.RequiredRole) && !System.Threading.Thread.CurrentPrincipal.IsInRole(item.RequiredRole))
                    continue;

                %><li><a href="<%= item.Uri %>"><%= item.Title %></a></li><%
            }
            %></ul><%
        }
    %>
</div>

Still horrible and completely unreadable tag soup.


Improvement number 2: use editor/display templates:

In ~/Views/Home/DisplayTemplates/MenuItem.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CompanyName.Framework.Web.MenuItem>" %>

<% if (!string.IsNullOrEmpty(Model.RequiredRole) &&
       System.Threading.Thread.CurrentPrincipal.IsInRole(Model.RequiredRole)) { %>
    <li>
        <a href="<%= Model.Uri %>"><%= Model.Title %></a>
    </li>
<% } %>

And then in your main view:

<div id="main-menu" class="menu">
    <ul>
        <%= Html.DisplayForModel() %>
    </ul>
</div>

Improvement number 3: Avoid coding business rules in a view. So in your view model add a property:

public bool IsLinkVisible
{
    get
    {
        return !string.IsNullOrEmpty(RequiredRole) &&
               Thread.CurrentPrincipal.IsInRole(RequiredRole);
    }
}

so that your display template now looks like this:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CompanyName.Framework.Web.MenuItem>" %>
<% if (Model.IsLinkVisible) { %>
    <li>
        <a href="<%= Model.Uri %>"><%= Model.Title %></a>
    </li>
<% } %>

Improvement number 4: Write a custom HTML helper to render this anchor because writing C# in a view is still ugly and untestable:

public static class HtmlExtensions
{
    public static MvcHtmlString MenuItem(this HtmlHelper<MenuItem> htmlHelper)
    {
        var menuItem = htmlHelper.ViewData.Model;
        if (!menuItem.IsLinkVisible)
        {
            return MvcHtmlString.Empty;
        }
        var li = new TagBuilder("li");
        var a = new TagBuilder("a");
        a.MergeAttribute("href", menuItem.Uri);
        a.SetInnerText(menuItem.Title);
        li.InnerHtml = a.ToString();
        return MvcHtmlString.Create(li.ToString());
    }
}

and finally your display template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CompanyName.Framework.Web.MenuItem>" %>
<%= Html.MenuItem() %>
like image 194
Darin Dimitrov Avatar answered Dec 29 '22 07:12

Darin Dimitrov