web-dev-qa-db-fra.com

Comment ajouter une classe "active" à Html.ActionLink dans ASP.NET MVC

J'essaie d'ajouter une classe "active" à ma barre de navigation d'amorçage dans MVC, mais ce qui suit ne montre pas la classe active lorsqu'elle est écrite comme ceci:

<ul class="nav navbar-nav">
  <li>@Html.ActionLink("Home", "Index", "Home", null, new {@class="active"})</li>
  <li>@Html.ActionLink("About", "About", "Home")</li>
  <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

Cela résout ce qui ressemble à une classe correctement formatée, mais ne fonctionne pas:

<a class="active" href="/">Home</a>

Dans la documentation de Bootstrap, il est indiqué que les balises 'a' ne doivent pas être utilisées dans la barre de navigation, mais la procédure ci-dessus explique comment, à mon avis, est la bonne façon d'ajouter une classe à un fichier Html.ActionLink. Est-ce qu'il y a une autre façon (rangée) de faire ça? 

107
Gillespie

Dans Bootstrap, la classe active doit être appliquée à l'élément <li> et non au <a>. Voir le premier exemple ici: http://getbootstrap.com/components/#navbar

La façon dont vous gérez votre style d'interface utilisateur en fonction de ce qui est actif ou non n'a rien à voir avec l'assistant ActionLink d'ASP.NET MVC. C’est la bonne solution pour suivre la construction du framework Bootstrap.

<ul class="nav navbar-nav">
    <li class="active">@Html.ActionLink("Home", "Index", "Home")</li>
    <li>@Html.ActionLink("About", "About", "Home")</li>
    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

Modifier:

Etant donné que vous réutiliserez probablement votre menu sur plusieurs pages, il serait judicieux de disposer d’un moyen d’appliquer automatiquement la classe sélectionnée en fonction de la page actuelle plutôt que de copier le menu plusieurs fois et de le faire manuellement. 

Le moyen le plus simple consiste simplement à utiliser les valeurs contenues dans ViewContext.RouteData, à savoir les valeurs Action et Controller. Nous pourrions construire sur ce que vous avez actuellement avec quelque chose comme ceci:

<ul class="nav navbar-nav">
    <li class="@(ViewContext.RouteData.Values["Action"].ToString() == "Index" ? "active" : "")">@Html.ActionLink("Home", "Index", "Home")</li>
    <li class="@(ViewContext.RouteData.Values["Action"].ToString() == "About" ? "active" : "")">@Html.ActionLink("About", "About", "Home")</li>
    <li class="@(ViewContext.RouteData.Values["Action"].ToString() == "Contact" ? "active" : "")">@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

Ce n'est pas beau dans le code, mais ça va faire le travail et vous permettre d'extraire votre menu dans une vue partielle si vous le souhaitez. Il existe des moyens de le faire de manière beaucoup plus propre, mais comme vous commencez tout juste, je vais en rester là. Bonne chance d'apprendre ASP.NET MVC!


Édition tardive:

Cette question semble générer un peu de trafic, j'ai donc décidé d'introduire une solution plus élégante utilisant une extension HtmlHelper.

24/03/2015: Il a fallu réécrire cette méthode pour autoriser plusieurs actions et contrôleurs déclenchant le comportement sélectionné, ainsi que la gestion de l'appel de la méthode depuis une vue partielle de l'action enfant, car je pensais que je partagerais la mise à jour!

public static string IsSelected(this HtmlHelper html, string controllers = "", string actions = "", string cssClass = "selected")
{
    ViewContext viewContext = html.ViewContext;
    bool isChildAction = viewContext.Controller.ControllerContext.IsChildAction;

    if (isChildAction)
        viewContext = html.ViewContext.ParentActionViewContext;

    RouteValueDictionary routeValues = viewContext.RouteData.Values;
    string currentAction = routeValues["action"].ToString();
    string currentController = routeValues["controller"].ToString();

    if (String.IsNullOrEmpty(actions))
        actions = currentAction;

    if (String.IsNullOrEmpty(controllers))
        controllers = currentController;

    string[] acceptedActions = actions.Trim().Split(',').Distinct().ToArray();
    string[] acceptedControllers = controllers.Trim().Split(',').Distinct().ToArray();

    return acceptedActions.Contains(currentAction) && acceptedControllers.Contains(currentController) ?
        cssClass : String.Empty;
}

Fonctionne avec .NET Core:

public static string IsSelected(this IHtmlHelper htmlHelper, string controllers, string actions, string cssClass = "selected")
{
    string currentAction = htmlHelper.ViewContext.RouteData.Values["action"] as string;
    string currentController = htmlHelper.ViewContext.RouteData.Values["controller"] as string;

    IEnumerable<string> acceptedActions = (actions ?? currentAction).Split(',');
    IEnumerable<string> acceptedControllers = (controllers ?? currentController).Split(',');

    return acceptedActions.Contains(currentAction) && acceptedControllers.Contains(currentController) ?
        cssClass : String.Empty;
}

Exemple d'utilisation:

<ul>
    <li class="@Html.IsSelected(actions: "Home", controllers: "Default")">
        <a href="@Url.Action("Home", "Default")">Home</a>
    </li>
    <li class="@Html.IsSelected(actions: "List,Detail", controllers: "Default")">
        <a href="@Url.Action("List", "Default")">List</a>
    </li>
</ul>
256
dom

Extension:

public static MvcHtmlString LiActionLink(this HtmlHelper html, string text, string action, string controller)
{
    var context = html.ViewContext;
    if (context.Controller.ControllerContext.IsChildAction)
        context = html.ViewContext.ParentActionViewContext;
    var routeValues = context.RouteData.Values;
    var currentAction = routeValues["action"].ToString();
    var currentController = routeValues["controller"].ToString();

    var str = String.Format("<li role=\"presentation\"{0}>{1}</li>",
        currentAction.Equals(action, StringComparison.InvariantCulture) &&
        currentController.Equals(controller, StringComparison.InvariantCulture) ?
        " class=\"active\"" :
        String.Empty, html.ActionLink(text, action, controller).ToHtmlString()
    );
    return new MvcHtmlString(str);
}

Usage:

<ul class="nav navbar-nav">
    @Html.LiActionLink("About", "About", "Home")
    @Html.LiActionLink("Contact", "Contact", "Home")
</ul>
28
Prof

J'ai réussi à le faire en ajoutant un paramètre de sac de vue dans asp.net mvc. Voici ce que j'ai fait

Ajout de ViewBag.Current = "Scheduler"; comme paramètre dans chaque page

Dans la page de mise en page

<ul class="nav navbar-nav">
     <li class="@(ViewBag.Current == "Scheduler" ? "active" : "") "><a href="@Url.Action("Index","Scheduler")" target="_self">Scheduler</a></li>
 </ul>

Cela a résolu mon problème.

18
Damith

Peut-être un peu tard. Mais espérons que cela aide.

public static class Utilities
{
    public static string IsActive(this HtmlHelper html, 
                                  string control,
                                  string action)
    {
        var routeData = html.ViewContext.RouteData;

        var routeAction = (string)routeData.Values["action"];
        var routeControl = (string)routeData.Values["controller"];

        // both must match
        var returnActive = control == routeControl &&
                           action == routeAction;

        return returnActive ? "active" : "";
    }
}

Et utilisation comme suit:

<div class="navbar-collapse collapse">
    <ul class="nav navbar-nav">
        <li class='@Html.IsActive("Home", "Index")'>
            @Html.ActionLink("Home", "Index", "Home")
        </li>
        <li class='@Html.IsActive("Home", "About")'>
            @Html.ActionLink("About", "About", "Home")
        </li>
        <li class='@Html.IsActive("Home", "Contact")'>
            @Html.ActionLink("Contact", "Contact", "Home")
        </li>
    </ul>
</div>

Vous avez obtenu la référence de http://www.codingeverything.com/2014/05/mvcbootstrapactivenavbar.html

11
Virag Dilip Desai

Je sais que cette question est ancienne, mais je voudrais juste ajouter ma voix ici. Je pense que c'est une bonne idée de laisser savoir au contrôleur de la vue si un lien est actif ou non. 

Je voudrais juste définir une valeur unique pour chaque vue dans l'action du contrôleur. Par exemple, si je voulais activer le lien de la page d'accueil, je procéderais de la manière suivante:

public ActionResult Index()
{            
    ViewBag.Title = "Home";
    ViewBag.Home = "class = active";
    return View();
}

Ensuite, à mon avis, j'écrirai quelque chose comme ceci:

<li @ViewBag.Home>@Html.ActionLink("Home", "Index", "Home", null, new { title = "Go home" })</li>

Lorsque vous accédez à une autre page, par exemple Programmes, ViewBag.Home n’existe pas (à la place, ViewBag.Programs existe); par conséquent, rien n'est rendu, pas même class = "". Je pense que c'est plus propre à la fois pour la maintenabilité et la propreté. J'ai tendance à toujours vouloir laisser la logique de côté autant que possible.

6
mapussah

Vous pouvez essayer ceci: Dans mon cas, je charge le menu de la base de données en fonction de l’accès basé sur les rôles, écrivez le code sur chaque vue, quel menu vous souhaitez activer en fonction de votre vue. 

<script type="text/javascript">
        $(document).ready(function () {         
            $('li.active active-menu').removeClass('active active-menu');
            $('a[href="/MgtCustomer/Index"]').closest('li').addClass('active active-menu');
        });
</script>
3
atik sarker

Cette solution est simple pour Asp.net MCV 5.

  1. Créez une classe statique, par exemple Utilitarios.cs.

  2. Dans la classe, créez une méthode statique:

    public static string IsLinkActive(this UrlHelper url, string action, string controller)
    {
        if (url.RequestContext.RouteData.Values["controller"].ToString() == controller &&
            url.RequestContext.RouteData.Values["action"].ToString() == action)
        {
            return "active";
        }
    
        return "";
    }
    
  3. appeler comme ça

    <ul class="sidebar-menu" data-widget="tree">
        <li class="header">HEADER</li>
        <li class="@Url.IsLinkActive("Index", "Home")">
            <a href="@Url.Action("Index", "Home")"><i class="fa fa-link"></i> <span>Home</span></a>
        </li>
        <li class="@Url.IsLinkActive("About", "Home")">
            <a href="@Url.Action("About", "Home")"><i class="fa fa-link"></i><span>About</span></a>
        </li>
    </ul>
    

est possible avec une fonction lambda

@{
string controllerAction = ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString();
    Func<string, string> IsSelected= x => x==controllerAction ? "active" : "";
}

puis utilisation

 @Html.ActionLink("Inicio", "Index", "Home", new { area = "" }, new { @class = IsSelected("HomeIndex")})
2
henoc salinas

Ajoutez '.ToString' pour améliorer la comparaison sur ASP.NET MVC

<ul class="nav navbar-nav">
<li class="@(ViewContext.RouteData.Values["Action"].ToString() == "Index" ? "active" : "")">@Html.ActionLink("Home", "Index", "Home")</li>
<li class="@(ViewContext.RouteData.Values["Action"].ToString() == "About" ? "active" : "")">@Html.ActionLink("About", "About", "Home")</li>
<li class="@(ViewContext.RouteData.Values["Action"].ToString() == "Contact" ? "active" : "")">@Html.ActionLink("Contact", "Contact", "Home")</li>

-

2
migovilo

Nous pouvons également créer UrlHelper à partir de RequestContext, que nous pouvons obtenir à partir de MvcHandler lui-même. Par conséquent, je pense que quelqu'un qui souhaite conserver cette logique dans les modèles Razor de la manière suivante serait utile:

  1. Dans la racine du projet, créez un dossier nommé AppCode
  2. Créez un fichier nommé HtmlHelpers.cshtml
  3. Créez un assistant ici:

    @helper MenuItem(string action, string controller)
    {
         var mvcHandler = Context.CurrentHandler as MvcHandler;
         if (mvcHandler != null)
         {
             var url = new UrlHelper(mvcHandler.RequestContext);
             var routeData = mvcHandler.RequestContext.RouteData;
             var currentAction = routeData.Values["action"].ToString();
             var currentController = routeData.Values["controller"].ToString();
             var isCurrent = string.Equals(currentAction, action, StringComparison.InvariantCultureIgnoreCase) &&
                             string.Equals(currentController, controller, StringComparison.InvariantCultureIgnoreCase);
    
            <div class="@(isCurrent ? "active" : "")">
                <div>@url.Action(action, controller)</div>
            </div>
         }   
    }
    
  4. Ensuite, nous pouvons utiliser sur nos vues comme ceci:

    @HtmlHelpers.MenuItem("Default", "Home")
    

J'espère que ça aide quelqu'un.

1
Oleksii Aza

Compte tenu de ce que Damith a posté, j'aime bien penser que vous pouvez simplement être considéré comme actif par le Viewbag.Title (la meilleure pratique consiste à le renseigner dans vos pages de contenu, ce qui permettra à votre page _Layout.cshtml de contenir vos barres de liens). Notez également que si vous utilisez les éléments de sous-menu cela fonctionne aussi très bien:

<li class="has-sub @(ViewBag.Title == "Dashboard 1" || ViewBag.Title == "Dashboard 2" ? "active" : "" )">
    <a href="javascript:;">
        <b class="caret"></b>
        <i class="fa fa-th-large"></i>
        <span>Dashboard</span>
    </a>
    <ul class="sub-menu">
        <li class="@(ViewBag.Title == "Dashboard 1" ? "active" : "")"><a href="index.html">Dashboard v1</a></li>
        <li class="@(ViewBag.Title == "Dashboard 2" ? "active" : "")"><a href="index_v2.html">Dashboard v2</a></li>
    </ul>
</li>
1
Rafe Smith

Je cherchais aussi une solution et jQuery m'a beaucoup aidé. Tout d'abord, vous devez donner les identifiants à vos éléments <li>

<li id="listguides"><a href='/Guides/List'>List Guides</a></li>

Ceci fait, vous pouvez indiquer à jQuery quel élément <li> doit être 'sélectionné' dans votre vue.

@section Scripts{
    <script type="text/javascript">
        $('#listguides').addClass('selected');
    </script>
}

Remarque: vous devez avoir @RenderSection("scripts", required: false) dans votre page de présentation, juste avant la balise </body> pour ajouter cette section.

1
Doğa Gençer

J'ai modifié la réponse de dom"pas jolie" et l'ai rendue encore plus laide. Parfois, deux contrôleurs ont des noms d'action conflictuels (c'est-à-dire Index), alors je le fais:

<ul class="nav navbar-nav">
  <li class="@(ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString() == "HomeIndex" ? "active" : "")">@Html.ActionLink("Home", "Index", "Home")</li>
  <li class="@(ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString() == "AboutIndex" ? "active" : "")">@Html.ActionLink("About", "Index", "About")</li>
  <li class="@(ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString() == "ContactHome" ? "active" : "")">@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>
1
DeveloperDan

la réponse de @dombenoit fonctionne. Bien que cela introduit du code à maintenir. Vérifiez cette syntaxe:

using (var nav = Html.Bootstrap().Begin(new Nav().Style(NavType.NavBar).SetLinksActiveByControllerAndAction()))
{
    @nav.ActionLink("Link 1", "action1")
    @nav.ActionLink("Link 2", "action2")
    @nav.Link("External Link", "#")
}

Notez l'utilisation de la méthode .SetLinksActiveByControllerAndAction().

Si vous vous demandez ce qui rend cette syntaxe possible, consultez TwitterBootstrapMVC

1
Dmitry

J'ai réalisé que ce problème était commun à certains d'entre nous, alors j'ai publié ma propre solution en utilisant le paquet Nuget. Ci-dessous, vous pouvez voir comment cela fonctionne. J'espère que cela vous sera utile.

Note: Ce paquet de pépites est mon premier paquet. Donc, si vous voyez une erreur, merci de donner votre avis. Je vous remercie.

  1. Installez Package ou téléchargez le code source et ajoutez votre projet

    -Install-Package Betalgo.MvcMenuNavigator
    
  2. Ajoutez vos pages à un enum

    public enum HeaderTop
    {
        Dashboard,
        Product
    }
    public enum HeaderSub
    {
        Index
    }
    
  3. Placez le filtre en haut de votre contrôleur ou de votre action

    [MenuNavigator(HeaderTop.Product, HeaderSub.Index)]
    public class ProductsController : Controller
    {
        public async Task<ActionResult> Index()
        {
            return View();
        }
    
        [MenuNavigator(HeaderTop.Dashboard, HeaderSub.Index)]
        public async Task<ActionResult> Dashboard()
        {
            return View();
        }
    }
    
  4. Et utilisez-le dans votre en-tête comme ceci 

    @{
    var headerTop = (HeaderTop?)MenuNavigatorPageDataNavigatorPageData.HeaderTop;
    var headerSub = (HeaderSub?)MenuNavigatorPageDataNavigatorPageData.HeaderSub;
    }
    <div class="nav-collapse collapse navbar-collapse navbar-responsive-collapse">
    <ul class="nav navbar-nav">
        <li class="@(headerTop==HeaderTop.Dashboard?"active selected open":"")">
            <a href="@Url.Action("Index","Home")">Dashboard</a>
        </li>
        <li class="@(headerTop==HeaderTop.Product?"active selected open":"")">
            <a href="@Url.Action("Index", "Products")">Products</a>
        </li>
    </ul>
    

Plus d'infos: https://github.com/betalgo/MvcMenuNavigator

1
Tolga Kayhan

J'ai fait ça:

fait une aide dans la vue partielle du men

 @helper RouterLink(string action, string controller)
{
    var IsActive = ViewContext.RouteData.Values["Controller"].ToString() == controller && ViewContext.RouteData.Values["Action"].ToString() == action;
    <text>href="@Url.Action(action, controller)"</text>if (IsActive){ <text>class="active"</text>}
}

Puis utilisé dans la balise anchor comme ceci:

<li><a @RouterLink("Index","Home")>Home</a></li>

Mon application ne contenait aucune zone, mais elle peut également être incluse comme une autre variable dans la fonction d'assistance. Et j'ai eu le passage de la classe active à la balise anchor à mon avis. Mais li peut également être configuré comme ceci.

0
Anup Sharma

J'ai créé une extension HtmlHelper qui ajoute une méthode ActiveActionLink pour ceux d'entre vous qui souhaitent ajouter la classe "active" au lien plutôt que le <li> entourant le lien.


public static class LinkExtensions
{
    public static MvcHtmlString ActiveActionLink(this HtmlHelper html, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
    {
        return ActiveActionLink(html, linkText, actionName, controllerName, new RouteValueDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static MvcHtmlString ActiveActionLink(this HtmlHelper html, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
    {
        const string activeClassName = "active";

        var routeData = html.ViewContext.RouteData;

        var routeAction = (string)routeData.Values["action"];
        var routeController = (string)routeData.Values["controller"];

        var active = controllerName.Equals(routeController) && actionName.Equals(routeAction);

        if (active)
        {
            var @class = (string)htmlAttributes["class"];

            htmlAttributes["class"] = string.IsNullOrEmpty(@class)
                ? activeClassName
                : @class + " active";
        }

        var link = html.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);

        return link;
    }
}

L'utilisation est la suivante:

@Html.ActiveActionLink("Home", "Index", "Home", new { area = "" }, new { @class = "nav-item nav-link" })
0
crush

J'espère que cela vous aidera. Voici comment votre menu peut être:

 <ul class="nav navbar-nav">
   <li><a href="@Url.Action("Index", "Home")" class="@IsMenuSelected("Index","Home", "active")">Home</a></li>
   <li><a href="@Url.Action("About", "Home")" class="@IsMenuSelected("About", "Home", "active")">About</a></li>
   <li><a href="@Url.Action("Contact", "Home")" class="@IsMenuSelected("Contact", "Home", "active")">Contact</a></li>
 </ul>

Et ajoutez ci-dessous la fonction d'assistance MVC sous la balise html. 

 @helper IsMenuSelected(string actionName, string controllerName, string className)
    {
            var conname = ViewContext.RouteData.Values["controller"].ToString().ToLower();
            var actname = ViewContext.RouteData.Values["action"].ToString().ToLower();

            if (conname == controllerName.ToLower() && actname == actionName.ToLower())
        {
            @className;
        }
    }

J'ai référé la réponse de http://questionbox.in/add-active-class-menu-link-html-actionlink-asp-net-mvc/

0
Ganesh Todkar

Je voudrais proposer cette solution qui est basée sur la première partie de la réponse de Dom.

Nous définissons d’abord deux variables, "action" et "controller" et les utilisons pour déterminer le lien actif:

{ string controller = ViewContext.RouteData.Values["Controller"].ToString();
string action = ViewContext.RouteData.Values["Action"].ToString();}

Et alors:

<ul class="nav navbar-nav">
    <li class="@((controller == "Home" && action == "Index") ? "active" : "")">@Html.ActionLink("Home", "Index", "Home")</li>
    <li class="@((controller == "Home" && action == "About") ? "active" : "")">@Html.ActionLink("About", "About", "Home")</li>
    <li class="@((controller == "Home" && action == "Contact") ? "active" : "")">@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>

Maintenant, il semble plus agréable et pas besoin de solutions plus complexes.

0
Osama Alsadi

s’il n’affiche pas du tout, la raison en est que vous avez besoin de deux signes @:

@@class

MAIS, je pense que vous pourriez avoir besoin d’avoir la classe active sur la balise "li" et non sur la balise "a". selon trop de documents d'amorçage ( http://getbootstrap.com/components/#navbar-default ):

<ul class="nav navbar-nav">
  <li class="active"><a href="#">Home</a></li>
  <li><a href="#">Profile</a></li>
  <li><a href="#">Messages</a></li>
</ul>

donc votre code sera:

<ul class="nav navbar-nav">
  <li class="active">@Html.ActionLink("Home", "Index", "Home", null)</li>
  <li>@Html.ActionLink("About", "About", "Home")</li>
  <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>
0
JC Lizard

Ma solution à ce problème est 

<li class="@(Context.Request.Path.Value.ToLower().Contains("about") ? "active " : "" ) nav-item">
                <a class="nav-link" asp-area="" asp-controller="Home" asp-action="About">About</a>
            </li>

Une meilleure façon peut être d’ajouter une méthode d’extension HTML pour renvoyer le chemin actuel à comparer avec link

0
Mudasar Rauf

J'ai faire la combinaison des réponses ci-dessus et fait ma solution. 

Alors.. 

D'abord dans le bloc rasoir, créez une variable chaîne qui contiendra le nom de la valeur du contrôleur et l'action appelée par l'utilisateur. 

    @{
        string controllerAction =  ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString(); 
    }

Ensuite, utilisez une combinaison de code HTML et de code Razor: 

    <ul class="nav navbar-nav">
        <li class="@(controllerAction == "HomeIndex" ? "active" : "" )">@Html.ActionLink("Home", "Index", "Home")</li>
        <li class="@(controllerAction == "AboutIndex" ? "active" : "" )">@Html.ActionLink("About", "Index", "About")</li>
        <li class="@(controllerAction == "HomeContact" ? "active" : "" )">@Html.ActionLink("Contact", "Contact", "Home")</li>
    </ul>

Je pense que cela est utile car vous n'avez pas besoin d'accéder à "ViewContext.RouteData.Values" à chaque fois pour obtenir le nom du contrôleur et le nom de l'action. 

0
Nihad Delic