23

I am a student and quite new to ASP.NET MVC and I come from ASP.NET Web Form. (Used to it)

I got a list :

<ul class="sidebar bg-grayDark">
    <li class="active">
        <a href="@Url.Action("Index", "Home")">
            <span class="mif-home icon"></span>
            <span class="title">Home</span>
        </a>
    </li>
    <li class="bg-hover-black">
        <a href="@Url.Action("Index", "Product")">
            <span class="mif-shop icon"></span>
            <span class="title">Products</span>
            <span class="counter">14</span>
        </a>
    </li>
    <li class="bg-hover-black">
        <a href="@Url.Action("Index", "Category")">
            <span class="mif-flow-cascade icon"></span>
            <span class="title">Categories</span>
            <span class="counter">9</span>
        </a>
    </li>
    <li class="bg-hover-black">
        <a href="@Url.Action("Index", "User")">
            <span class="mif-users icon"></span>
            <span class="title">Users</span>
            <span class="counter">1</span>
        </a>
    </li>
</ul>

My goal : By which view is rendered, I want to add "active" to the that has been clicked on. Example : I click on "Category", then Home loses his active class and Category has "active" added to his class. (and the reverse thing with "bg-hover-black")

I thought I could do it by checking the view actually rendered but I don't know how to do it. (I don't know how to check the actual view rendered, but using Razor to check conditions is okay)

I tried with JavaScript first :


    $(function () {
        $('.sidebar').on('click', 'li', function () {
            if (!$(this).hasClass('active')) {
                $('.sidebar li').removeClass('active');
                $(this).addClass('active');
            }
        })
    })
    

But it doesn't work because when the page loads, the html is re-rendered with "active" for the Home part. (If I remove "active" for Home, then nothing will be active onClick, except between click and page load).

Have you any solution ? I searched a lot on the web but found nothing to help me.

Sorry for any english mistakes, I'm still learning it :).

Thanks,

Hellcat8.

Hellcat8
  • 538
  • 2
  • 5
  • 17
  • The beginning of the post doesn't show.. Here it is : "Hello everybody, I am a student and quite new to ASP.NET MVC[...]" – Hellcat8 Jul 01 '15 at 08:42

4 Answers4

29

Since you are using the convention where your page is named after the controller you can do this in razor to get the controller/page name:

@{
 var pageName = ViewContext.RouteData.Values["controller"].ToString();
}

<ul class="sidebar bg-grayDark">
    <li class="@(pageName == "Home" ? "active" : "")">
        <a href="@Url.Action("Index", "Home")">
            <span class="mif-home icon"></span>
            <span class="title">Home</span>
        </a>
    </li>
    <li class="bg-hover-black @(pageName == "Product" ? "active" : "")">
        <a href="@Url.Action("Index", "Product")">
            <span class="mif-shop icon"></span>
            <span class="title">Products</span>
            <span class="counter">14</span>
        </a>
    </li>
    <li class="bg-hover-black @(pageName == "Category" ? "active" : "")">
        <a href="@Url.Action("Index", "Category")">
            <span class="mif-flow-cascade icon"></span>
            <span class="title">Categories</span>
            <span class="counter">9</span>
        </a>
    </li>
    <li class="bg-hover-black @(pageName == "User" ? "active" : "")">
        <a href="@Url.Action("Index", "User")">
            <span class="mif-users icon"></span>
            <span class="title">Users</span>
            <span class="counter">1</span>
        </a>
    </li>
</ul>

This will set your active class on the page server side removing the need to do this client side with javascript.

omajid
  • 14,165
  • 4
  • 47
  • 64
Anish Patel
  • 4,332
  • 1
  • 30
  • 45
8

Here is my approach for a cascaded Bootstrap dropdown submenu, decorated with active classes in the _layout.cshtml file of a Razor Pages project.

Major elements of this solution:

  • Get the current page route from ViewContext.RouteData.Values["page"].
  • Use Anchor Tag Helpers instead of @Url.Action().

Code:

<ul class="nav navbar-nav">
    @{
        String pageRoute = ViewContext.RouteData.Values["page"].ToString();
    }
    <li class="dropdown @( pageRoute.Contains("/CustomerModel/") ? "active" : "" )">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Customer-Model <span class="caret"></span></a>
        <ul class="dropdown-menu">
            <li class="@( pageRoute.Contains("/Customers/") ? "active" : "" )"><a asp-page="/CustomerModel/Customers/Index">Customers</a></li>
            <li class="@( pageRoute.Contains("/Partners/")  ? "active" : "" )"><a asp-page="/CustomerModel/CustomerPermissions/Index">CustomerPermissions</a></li>
        </ul>
    </li>
    <li class="dropdown @( pageRoute.Contains("/StaffModel/") ? "active" : "" )">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Staff-Model <span class="caret"></span></a>
        <ul class="dropdown-menu">
            <li class="@( pageRoute.Contains("/Staff/")              ? "active" : "" )"><a asp-page="/StaffModel/Staff/Index">Staff</a></li>
            <li class="@( pageRoute.Contains("/StaffGroups/")        ? "active" : "" )"><a asp-page="/StaffModel/StaffGroups/Index">StaffGroups</a></li>
            <li class="@( pageRoute.Contains("/PermissionsGroups/")  ? "active" : "" )"><a asp-page="/StaffModel/PermissionsGroups/Index">PermissionsGroups</a></li>
            <li class="@( pageRoute.Contains("/AllowedModules/")     ? "active" : "" )"><a asp-page="/StaffModel/AllowedModules/Index">AllowedModules</a></li>
            <li class="@( pageRoute.Contains("/AllowedServices/")    ? "active" : "" )"><a asp-page="/StaffModel/AllowedServices/Index">AllowedServices</a></li>
        </ul>
    </li>
</ul>
Jpsy
  • 20,077
  • 7
  • 118
  • 115
5

To get the name of the active action or controller use the following

var controllerName = ViewContext.RouteData.Values["controller"].ToString();
var actionName = ViewContext.RouteData.Values["action"].ToString();

and then you can give the class for a li using the following code:

<li class="@(actionName == "HomePage" ? "active":"")"><a href="~/Account/HomePage">Home</a></li>

or you can check both of them as following:

 <li class="@(controllerName =="Account" && actionName == "HomePage" ? "active":"")"><a href="~/Account/HomePage">Home</a></li>
khaled saleh
  • 470
  • 7
  • 18
2

To confirm: You're changing the page when the user clicks on the item.

Because of this, your javascript will run, but then the whole page is re-written and you're back to the first state (ie with Home active as that's in the markup).

To check the current page, you can use location.href and compare it with the href url, something like:

$(function() {
    $("ul.sidebar>li").removeClass("active");  // or just not have active in the markup
    $("li>a[href=" + location.href + "]").closest("li").addClass("active");
});

Alternatively, and this would be more robust, you could pass a token (string, enum or const) in with the viewmodel and check that, eg:

<ul class='sidebar'>
    <li data-page='home'...
    ...
    <li data-page='categories'...

then

$(function() { 
    $("li[data-page=@Model.PageName]").addClass("active")

(or just in the markup...)

    <li data-page='categories' @(Model.PageName == 'Categories' ? "class=active" : "")>
freedomn-m
  • 27,664
  • 8
  • 35
  • 57