How to add “active” class to Html.ActionLink in AS

2019-01-01 06:47发布

I'm trying to add an"active" class to my bootstrap navbar in MVC, but the following doesn't show the active class when written like this:

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

This resolves to what looks like a correctly formatted class, but doesn't work:

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

In the Bootstrap documentation it states that 'a' tags shouldn't be used in the navbar, but the above is how I believe is the correct way of adding a class to an Html.ActionLink. Is there another (tidy) way I can do this?

21条回答
时光乱了年华
2楼-- · 2019-01-01 07:07

I would like to propose this solution which is based on the first part of Dom's answer.

We first define two variables, "action" and "controller" and use them to determine the active link:

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

And then:

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

Now it looks nicer and no need for more complex solutions.

查看更多
不流泪的眼
3楼-- · 2019-01-01 07:07

Considering what Damith posted, I like to think you can just qualify active by the Viewbag.Title (best practice is to populate this in your content pages allowing your _Layout.cshtml page to hold your link bars). Also note that if you are using sub-menu items it also works fine:

<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>
查看更多
余生请多指教
4楼-- · 2019-01-01 07:08

In Bootstrap the active class needs to be applied to the <li> element and not the <a>. See the first example here: http://getbootstrap.com/components/#navbar

The way you handle your UI style based on what is active or not has nothing to do with ASP.NET MVC's ActionLink helper. This is the proper solution to follow how the Bootstrap framework was built.

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

Edit:

Since you will most likely be reusing your menu on multiple pages, it would be smart to have a way to apply that selected class automatically based on the current page rather than copy the menu multiple times and do it manually.

The easiest way is to simply use the values contained in ViewContext.RouteData, namely the Action and Controller values. We could build on what you currently have with something like this:

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

It's not pretty in code, but it'll get the job done and allow you to extract your menu into a partial view if you like. There are ways to do this in a much cleaner way, but since you're just getting started I'll leave it at that. Best of luck learning ASP.NET MVC!


Late edit:

This question seems to be getting a bit of traffic so I figured I'd throw in a more elegant solution using an HtmlHelper extension.

Edit 03-24-2015: Had to rewrite this method to allow for multiple actions and controllers triggering the selected behavior, as well as handling for when the method is called from a child action partial view, thought I'd share the update!

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

Works with .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;
}

Sample usage:

<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>
查看更多
怪性笑人.
5楼-- · 2019-01-01 07:08

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>
查看更多
流年柔荑漫光年
6楼-- · 2019-01-01 07:11

I also was looking for a solution and jQuery helped pretty much. First, you need to give 'id's to your <li> elements.

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

After doing this in your layout page, you can tell jQuery which <li> element should be 'selected' in your view.

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

Note: You need to have @RenderSection("scripts", required: false) in your layout page, just before the </body> tag to add that section.

查看更多
刘海飞了
7楼-- · 2019-01-01 07:16

You can try this: In my case i am loading menu from database based on role based access, Write the code on your every view which menu your want to active based on your view.

<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>
查看更多
登录 后发表回答