web-dev-qa-db-fra.com

Meilleure façon d'obtenir un lien de page actif dans MVC 3 Razor

Lorsque je veux qu'un lien de menu spécifique soit actif sur une page donnée, j'utilise cette approche dans Razor:

Sur la maquette principale, j'ai ces vérifications:

var active = ViewBag.Active;
const string ACTIVE_CLASS = "current";

if (active == "home")
{
    ViewBag.ActiveHome = ACTIVE_CLASS;
}
if (active == "products")
{
    ViewBag.ActiveProducts = ACTIVE_CLASS;
}

etc.

Le menu html de la mise en page principale:

<ul>
<li class="@ViewBag.ActiveHome"><a href="/">Home</a></li>
<li class="@ViewBag.ActiveProducts"><a href="@Url.Action("index", "products")">Products</a></li>
</ul>

Lors de la spécification de la page de présentation à utiliser sur une vue différente:

@{
    ViewBag.Active = "home";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Existe-t-il une meilleure approche pour séparer les liens actifs que celle que j'utilise actuellement?

54
Jens

Une meilleure approche consiste à utiliser un assistant HTML:

using System.Web.Mvc; 
using System.Web.Mvc.Html;

public static class MenuExtensions
{
    public static MvcHtmlString MenuItem(
        this HtmlHelper htmlHelper, 
        string text,
        string action, 
        string controller
    )
    {
        var li = new TagBuilder("li");
        var routeData = htmlHelper.ViewContext.RouteData;
        var currentAction = routeData.GetRequiredString("action");
        var currentController = routeData.GetRequiredString("controller");
        if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
            string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
        {
            li.AddCssClass("active");
        }
        li.InnerHtml = htmlHelper.ActionLink(text, action, controller).ToHtmlString();
        return MvcHtmlString.Create(li.ToString());
    }
}

et alors:

<ul>
    @Html.MenuItem("Home", "Home", "Home")
    @Html.MenuItem("Products", "Index", "Products")
</ul>

Pour que cela fonctionne, vos vues doivent reconnaître votre extension: Dans Web.config du dossier Vues, ​​ajoutez <add namespace="yourNamespacehere.Helpers" /> à l'intérieur de la balise namespaces. Générez ensuite votre projet, fermez et rouvrez la vue à laquelle vous ajoutez ceci.

puis, en fonction de l'action en cours et du contrôleur, l'assistant ajoutera ou non la classe active lors de la génération de l'ancre.

126
Darin Dimitrov

En développant l'exemple de Darin, voici la classe complète qui ajoute des paramètres facultatifs supplémentaires pour RouteValues ​​et HtmlAttributes sur l'aide. En réalité, il se comporte exactement comme le ActionLink de base.

using System;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace MYNAMESPACE.Helpers {
    public static class MenuExtensions {
        public static MvcHtmlString MenuItem(this HtmlHelper htmlHelper,
                                             string text, string action,
                                             string controller,
                                             object routeValues = null,
                                             object htmlAttributes = null) {
            var li = new TagBuilder("li");
            var routeData = htmlHelper.ViewContext.RouteData;
            var currentAction = routeData.GetRequiredString("action");
            var currentController = routeData.GetRequiredString("controller");
            if (string.Equals(currentAction,
                              action,
                              StringComparison.OrdinalIgnoreCase) &&
                string.Equals(currentController,
                              controller,
                              StringComparison.OrdinalIgnoreCase)) {
                li.AddCssClass("active");
            }
            if (routeValues != null) {
                li.InnerHtml = (htmlAttributes != null)
                    ? htmlHelper.ActionLink(text,
                                            action,
                                            controller,
                                            routeValues,
                                            htmlAttributes).ToHtmlString()
                    : htmlHelper.ActionLink(text, 
                                            action, 
                                            controller, 
                                            routeValues).ToHtmlString();
            }
            else {
                li.InnerHtml = htmlHelper.ActionLink(text, 
                                                     action, 
                                                     controller).ToHtmlString();
            }
            return MvcHtmlString.Create(li.ToString());
        }
    }
}

Et dans le fichier web.config du dossier View:

<system.web.webPages.razor>
  <Host ... />
  <pages ... >
    <namespaces>
      ...

      ...
      <add namespace="MYNAMESPACE.Helpers" />
    </namespaces>
  </pages>
</system.web.webPages.razor>
6
dperish

Mis à jour pour RC2 - Pour ceux qui se demandent comment faire cela dans MVC6/Asp.Net 5 - similaire mais légèrement différent. Il n'y a maintenant plus de MvcHtmlString et RouteData fonctionne complètement différemment. De plus, l'objet de contexte devrait maintenant être IHtmlContent plutôt que HtmlHelper.

using System;
using Microsoft.AspNet.Mvc.Rendering;

public static class MenuExtensions
{
    public static IHtmlContent MenuItem(
        this IHtmlHelper htmlHelper,
        string text,
        string action,
        string controller
    )
    {

        var li = new TagBuilder("li") { TagRenderMode = TagRenderMode.Normal };
        var routeData = htmlHelper.ViewContext.RouteData;
        var currentAction = routeData.Values["action"].ToString();
        var currentController = routeData.Values["controller"].ToString();

        if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
            string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
        {
            li.AddCssClass("active");
        }


        li.InnerHtml.AppendHtml(htmlHelper.ActionLink(text, action, controller));

        return li;


    }
}
2
Party Ark

Utilisez cet InnerHtml si vous souhaitez inclure la mise en forme HTML dans votre texte.

li.InnerHtml = "<a href=\"" + new UrlHelper(htmlHelper.ViewContext.RequestContext).Action(action, controller).ToString() + "\">" + text + "</a>";

le texte pourrait être "<b> gras </ b> normal";

2
edocetirwi

Ce code a très bien fonctionné pour moi, même sur un nouveau projet Visual Studio 2013 MVC5/Bootstrap. Notez également que vous pouvez modifier le li.AddCssClass ("active"); line pour pointer sur une classe personnalisée si vous souhaitez laisser la classe "active" de Bootstrap seule. J'ai ajouté un élément appelé "activemenu" dans le fichier Site.css du projet et y ai apporté les modifications de style que vous souhaitiez.

La ligne dans le code ci-dessus a juste été modifiée en ceci pour que tout fonctionne correctement:

li.AddCssClass("activemenu");

Dans Site.css, j'ai ajouté une classe simple pour mes besoins:

.activemenu {
    text-decoration: underline;
}

Sinon, vous pouvez changer la couleur de fond et/ou la bordure, etc ...

0
ggoodro

voici une extension sur la classe de Darin pour insérer du HTML dans le texte du lien plutôt qu'un simple texte

using System;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace YourNameSpaceHere
{
    public static class MenuExtensions
    {
        public static MvcHtmlString MenuItem(
            this HtmlHelper htmlHelper,
            string html,
            string action,
            string controller
        )
        {
            var li = new TagBuilder("li");
            var routeData = htmlHelper.ViewContext.RouteData;
            var currentAction = routeData.GetRequiredString("action");
            var currentController = routeData.GetRequiredString("controller");
            if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
                string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
            {
                li.AddCssClass("active");
            }
            //generate a unique id for the holder and convert it to string
            string holder = Guid.NewGuid().ToString();
            string anchor = htmlHelper.ActionLink(holder, action, controller).ToHtmlString();
            //replace the holder string with the html
            li.InnerHtml = anchor.Replace(holder, html);
            return MvcHtmlString.Create(li.ToString());
        }
    }
}

et l'utiliser comme ça:

<ul>
    @Html.MenuItem("<span class'ClassName'>Home</span>", "Home", "Home")
</ul>
0
MAK5