2010-10-15 20 views
4

Je suis assez nouveau pour ASP.Net et MVC.Aide ASP.Net MVC avec refactoring

J'ai obtenu le code suivant dans ma page principale:

<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> 

Puis-je déplacer le code à un autre fichier ou factoriser le code de quelque façon?

modifier:

Mon ApplicationController que tous les contrôleurs dérivent:

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); 
    } 
} 

Répondre

9

Voici quelques suggestions:

Numéro d'amélioration 1: modèles utilisent de vue et des vues fortement typées au lieu de ViewData

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

    return View(menus); 
} 

puis dans votre vue:

<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> 

Soupe aux tags toujours horrible et complètement illisible.


Numéro d'amélioration 2: utilisation éditeur/modèles d'affichage:

En ~/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> 
<% } %> 

Et puis dans votre vue principale:

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

Numéro d'amélioration 3: Évitez de coder les règles métier dans une vue. Donc, dans votre modèle de vue ajouter une propriété:

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

afin que votre modèle d'affichage ressemble maintenant à ceci:

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

Numéro amélioration 4: Ecrire un assistant HTML personnalisé pour rendre ce ancre parce qu'écrire C# dans une vue est toujours moche et impossible à tester:

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()); 
    } 
} 

et enfin votre modèle d'affichage:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CompanyName.Framework.Web.MenuItem>" %> 
<%= Html.MenuItem() %> 
+2

+1 belle amélioration flux !! –

+0

Le problème est que j'utilise mes BusinessObjects comme modèles pour la plupart des vues. Le MainMenu (et un ContextMenu) sont des propriétés sur ApplicationControlLer et sont affectés à ViewData avant l'exécution de ActionResult. Bien sûr, je pourrais faire un ApplicationViewData qui prend l'objet métier comme argument de type. Mais cela ne semble pas une solution propre comme les propriétés dans le contrôleur et l'utilisation de ViewData ["MainMenu"] dans la page maître, puisque seule la page maître est affectée par ce dernier. J'aime votre solution, mais comment puis-je l'utiliser sans avoir à utiliser des vues typées? – jgauffin

+0

S'il se trouve dans la page maître, cela signifie que toutes les vues ont besoin de cette propriété. Vous pouvez donc faire en sorte que tous vos modèles de vue dérivent d'un modèle de vue de base qui contient le menu en tant que propriété. Cette propriété peut ensuite être remplie dans un filtre d'action pour éviter de le faire dans toutes les actions, puis la page maître peut être fortement typée sur ce modèle de vue de base. –

3

Oui, vous pouvez simplement mettre ce bloc dans un fichier .ascx et utilisation:

<% html.RenderPartial("myPartialFile.asx"); %> 

Le ci-dessus suppose que myPartialFile.ascx se trouve dans le même dossier que votre page maître, généralement le dossier Views/Shared.

+0

En fait, le partiel doit être dans le même dossier OU dans le dossier Views/Shared.Pas besoin de bouger les deux là. Même si vous avez raison, les pages maîtres sont généralement partagées. –

+0

+1 - exactement ce que j'allais suggérer L :) –