Définition du lien actif dans une page Razor

Publié le 13/07/2014

Un √©l√©ment essentiel pour la compr√©hension de l'interface par l'utilisateur est de savoir o√Ļ il se trouve. Pour cela, il existe plusieurs patterns (breadcrumb, historique de navigation, ...): tout pour que l'utilisateur comprenne rapidement et en un coup d'oeil l'emplacement o√Ļ il se trouve et son contexte.

Un √©l√©ment √† combiner avec les id√©es ci-dessous est de placer le lien actif de la page en gras, afin d'avoir une bonne visualisation de "o√Ļ suis-je?".

L'impl√©mentation la plus simple consiste √† comparer le contexte de la requ√™te avec la page qui a √©t√©/est en cours de g√©n√©ration. Et plut√īt que d'effectuer ces v√©rifications directement dans le template de la page, nous pouvons d√©finir un helper, qui nous permettra de g√©n√©rer dynamiquement un √©l√©ment <li> auquel sera associ√©e la classe CSS active s'il correspond √† l'URL actuelle du navigateur.

Les paramètres nécessaires à la génération du tag HTML sont:

  1. Une méthode d'extension, pour que cela s'applique à n'importe quel instance de type HtmlHelper
  2. Le texte qui sera affiché dans notre élément
  3. L'action et le contr√īler (au sens MVC)
  4. Les routes
  5. D'éventuels attributs HTML

Tout ceci nous permettra d'appeler directement notre helper de la manière suivante:

@Html.MenuItem("Details", "Details", "Node", new { id = Model.ID }, null)

En pratique

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

namespace RPS.Web.Helpers
{
    public static class MenuExtensions
    {
        public static MvcHtmlString MenuItem(
            this HtmlHelper htmlHelper,
            string text,
            string action,
            string controller,
            object routeValues,
            object htmlAttributes
        )
        {
            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,
                routeValues,
                htmlAttributes
            ).ToHtmlString();

            return MvcHtmlString.Create(li.ToString());
        }
    }
}

Dernier problème: cette extension de méthode ne peut être appelée directement depuis la syntaxe Razor. Pour pallier à cela, il suffit d'ajouter le namespace défini ci-dessus dans le fichier Web.config qui se trouve dans le répertoire Views:

<system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Optimization" />
        <add namespace="System.Web.Routing" />
        <!-- On ajoute la référence vers le namespace à utiliser -->
        <add namespace="MyProject.Web.Helpers" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

On peut ensuite l'appeler directement dans le rendu HTML de la page (pas besoin de définir l'élément li, puisque celui-ci sera généré par le helper):

<ul id="entityLinksMenu">
	@Html.MenuItem("Details", "Details", "Node", new { id = Model.ID }, null)
	@Html.MenuItem("Display", "Display", "Node", new { id = Model.ID }, null)
</ul>

Et finalement, on ajoute une classe .active dans le style CSS, qui mettra le texte en gras:

#nodeLinksMenu li.active {
	font-weight: bold;
}

Références