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?
This question is related to
asp.net
css
asp.net-mvc
twitter-bootstrap
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>
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.
I modified dom's "not pretty" answer and made it uglier. Sometimes two controllers have the conflicting action names (i.e. Index) so I do this:
<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>
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.
[HtmlTargetElement("li", Attributes = "active-when")]
public class LiTagHelper : TagHelper
{
public string ActiveWhen { get; set; }
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContextData { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (ActiveWhen == null)
return;
var targetController = ActiveWhen.Split("/")[1];
var targetAction = ActiveWhen.Split("/")[2];
var currentController = ViewContextData.RouteData.Values["controller"].ToString();
var currentAction = ViewContextData.RouteData.Values["action"].ToString();
if (currentController.Equals(targetController) && currentAction.Equals(targetAction))
{
if (output.Attributes.ContainsName("class"))
{
output.Attributes.SetAttribute("class", $"{output.Attributes["class"].Value} active");
}
else
{
output.Attributes.SetAttribute("class", "active");
}
}
}
}
Include into your _ViewImports.cs:
@addTagHelper *, YourAssemblyName
Usage:
<li active-when="/Home/Index">
if is it is not showing at all, the reason is that you need two @ sign:
@@class
BUT, I believe you might need to have the active class on the "li" tag not on the "a" tag. according too bootstrap docs (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>
therefore your code will be:
<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>
May be little late. But hope this helps.
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" : "";
}
}
And usage as follow:
<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>
Got reference from http://www.codingeverything.com/2014/05/mvcbootstrapactivenavbar.html
is possible with a lambda function
@{
string controllerAction = ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString();
Func<string, string> IsSelected= x => x==controllerAction ? "active" : "";
}
then usage
@Html.ActionLink("Inicio", "Index", "Home", new { area = "" }, new { @class = IsSelected("HomeIndex")})
This solution is simple for Asp.net MCV 5.
Create a static class, for example Utilitarios.cs
.
Inside the Class create a static method:
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 "";
}
call like this
<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>
I created an HtmlHelper
extension that adds an ActiveActionLink
method for those of you who want to add the "active" class to the link itself rather than the <li>
surrounding the link.
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;
}
}
Usage is as follows:
@Html.ActiveActionLink("Home", "Index", "Home", new { area = "" }, new { @class = "nav-item nav-link" })
My Solution to this problem is
<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>
A better way may be adding an Html extension method to return the current path to be compared with link
the answer by @dombenoit works. Though it introduces some code to maintain. Check this syntax out:
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", "#")
}
Notice the use of .SetLinksActiveByControllerAndAction()
method.
If you wonder what makes this syntax possible, check out TwitterBootstrapMVC
I know this question is old, but I will just like to add my voice here. I believe it is a good idea to leave the knowledge of whether or not a link is active to the controller of the view.
I would just set a unique value for each view in the controller action. For instance, if I wanted to make the home page link active, I would do something like this:
public ActionResult Index()
{
ViewBag.Title = "Home";
ViewBag.Home = "class = active";
return View();
}
Then in my view, I will write something like this:
<li @ViewBag.Home>@Html.ActionLink("Home", "Index", "Home", null, new { title = "Go home" })</li>
When you navigate to a different page, say Programs, ViewBag.Home does not exist (instead ViewBag.Programs does); therefore, nothing is rendered, not even class="". I think this is cleaner both for maintainability and cleanliness. I tend to always want to leave logic out of the view as much as I can.
I manged to do this by adding a view bag parameter in asp.net mvc. Here what have i done
Added ViewBag.Current = "Scheduler";
like parameter in each page
In layout page
<ul class="nav navbar-nav">
<li class="@(ViewBag.Current == "Scheduler" ? "active" : "") "><a href="@Url.Action("Index","Scheduler")" target="_self">Scheduler</a></li>
</ul>
This solved my problem.
I did this:
Made a helper in menu partial view
@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>}
}
Then used it in the anchor tag like this:
<li><a @RouterLink("Index","Home")>Home</a></li>
My application had no areas but it can also be included as another variable in the helper function. And I had to pass the active class to the anchor tag in my view. But li
can also be configured like this.
I believe here is a cleaner and smalller code to do get the selected menu being "active":
<ul class="navbar-nav mr-auto">
<li class="nav-item @Html.IfSelected("Index")">
<a class="nav-link" href="@Url.Action("Index", "Home")">Home</a>
</li>
<li class="nav-item @Html.IfSelected("Controls")">
<a class="nav-link" href="@Url.Action("Controls", "Home")">MVC Controls</a>
</li>
<li class="nav-item @Html.IfSelected("About")">
<a class="nav-link" href="@Url.Action("About", "Home")">About</a>
</li>
</ul>
public static string IfSelected(this HtmlHelper html, string action)
{
return html
.ViewContext
.RouteData
.Values["action"]
.ToString() == action
? " active"
: "";
}
We also can create UrlHelper
from RequestContext which we can get from MvcHandler itself. Therefore I beleive for someone who wants to keep this logic in Razor templates following way would be helpful:
AppCode
. HtmlHelpers.cshtml
Create a helper in there:
@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>
}
}
Then we can use on our views like this:
@HtmlHelpers.MenuItem("Default", "Home")
Hope that it helps to someone.
I realized that this problem was a common problem for some of us, so I published my own solution using nuget package. Below you can see how it works. I hope that will be useful.
Note:This nuget package is my first package. So if you see a mistake, please give feedback. Thank you.
Install Package or download source code and add your Project
-Install-Package Betalgo.MvcMenuNavigator
Add your pages to an enum
public enum HeaderTop
{
Dashboard,
Product
}
public enum HeaderSub
{
Index
}
Put Filter to top of your Controllor or 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();
}
}
And use it In your header layout like this
@{
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>
More Info: https://github.com/betalgo/MvcMenuNavigator
Add '.ToString' to improve comparing on 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>
--
I have make combination of answers above and made my solution.
So..
First in razor block create one string variable which will contain name value of controller and action that is called by user.
@{
string controllerAction = ViewContext.RouteData.Values["Controller"].ToString() + ViewContext.RouteData.Values["Action"].ToString();
}
Then use combination of HTML and Razor code:
<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>
I think, that this is good because you don't need to access "ViewContext.RouteData.Values" each time to get controller name and action name.
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>
Most of the solutions on this question all require you to specify the 'page' on both the li
and the a
(via HtmlHelper) tag. Both @Wolles and @Crush's answers eliminated this duplication, which was nice, but they were using HtmlHelper extension methods instead of TagHelpers. And I wanted to use a TagHelper and support Razor Pages.
You can read my full post here (and get the source code), but the gist of it is:
<bs-menu-link asp-page="/Events/Index" menu-text="Events"></bs-menu-link>
Would render (if the link was active obviously):
<li class="active"><a href="/Events">Events</a></li>
My TagHelper leverages the AnchorTagHelper
internally (thus supporting asp-page, asp-controller, asp-action, etc. attributes). The 'checks' for active or not is similar to many of the answers to this post.
Most up-to-date answer
I have re-worked @crush's neat solution for an updated, ASP.NET Core and Bootstrap 4 compatible way to solve this problem based on an IHtmlHelper
extension method:
public static class LinkExtensions
{
public static IHtmlContent ActiveActionLink(this IHtmlHelper 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 IHtmlContent ActiveActionLink(this IHtmlHelper html, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
{
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);
using (var writer = new StringWriter())
{
writer.WriteLine($"<li class='nav-item {(active ? "active" : "")}'>");
html.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes).WriteTo(writer, HtmlEncoder.Default);
writer.WriteLine("</li>");
return new HtmlString(writer.ToString());
}
}
}
Usage
<nav class="navbar">
<div class="collapse navbar-collapse">
<ul class="navbar-nav">
@Html.ActiveActionLink("Home", "Index", "Home", null, new { @class = "nav-link" })
@Html.ActiveActionLink("About", "About", "Home", null, new { @class = "nav-link" })
@Html.ActiveActionLink("Contact", "Contact", "TimeTracking", null, new { @class = "nav-link" })
</ul>
</div>
</nav>
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>
Source: Stackoverflow.com