วิธีเพิ่มคลาส“ active” ใน Html.ActionLink ใน ASP.NET MVC


128

ฉันกำลังพยายามเพิ่มคลาส "active" ให้กับ bootstrap navbar ใน MVC แต่สิ่งต่อไปนี้ไม่แสดงคลาสที่ใช้งานอยู่เมื่อเขียนแบบนี้:

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

สิ่งนี้จะแก้ไขสิ่งที่ดูเหมือนคลาสที่มีรูปแบบถูกต้อง แต่ใช้ไม่ได้:

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

ในเอกสาร Bootstrap ระบุว่าไม่ควรใช้แท็ก 'a' ในแถบนำทาง แต่ข้างต้นเป็นวิธีที่ฉันเชื่อว่าเป็นวิธีที่ถูกต้องในการเพิ่มคลาสให้กับ Html.ActionLink มีวิธีอื่น (เป็นระเบียบ) ที่ฉันสามารถทำได้หรือไม่?


3
รหัสที่แก้ไขมี class = active อยู่ นั่นคือสิ่งที่คุณต้องการในคำถามของคุณไม่ใช่หรือ? คุณมีรหัสของคุณได้อย่างไรฉันจะเพิ่มชั้นเรียนได้อย่างไร
Matt Bodily

มึงไม่ชัด !!! ฉันไม่เข้าใจว่าอะไรคือปัญหาในการเพิ่มลงในระบบนำทางแม้ว่าฉันจะได้รับความรู้สึก แต่ก็น่าจะดี
JC Lizard

1
ปัญหาเกี่ยวกับรหัสที่แก้ไขแล้วคืออะไร? คุณคาดหวังอะไร คุณสามารถเจาะจงมากกว่า "ดูเหมือนจะไม่ทำงาน" ได้ไหม คำถามของคุณยังไม่ชัดเจนพอที่จะช่วยคุณได้ในตอนนี้
Dom

แก้ไข! ขอโทษที่ฉันหมายถึงคือคลาส "ใช้งานอยู่" ไม่แสดงเลย
Gillespie

2
เพียงแค่มองไปที่เอกสารของ Bootstrap และฉันคิดว่าคุณต้องเพิ่มactiveคลาสให้กับliองค์ประกอบไม่ใช่ไฟล์a. ดูตัวอย่างแรกที่นี่: getbootstrap.com/components/#navbar
dom

คำตอบ:


300

ใน Bootstrap activeคลาสจะต้องถูกนำไปใช้กับ<li>องค์ประกอบไม่ใช่ไฟล์<a>. ดูตัวอย่างแรกที่นี่: http://getbootstrap.com/components/#navbar

วิธีจัดการสไตล์ UI ของคุณตามสิ่งที่ใช้งานอยู่หรือไม่มีอะไรเกี่ยวข้องกับตัวActionLinkช่วยของ ASP.NET MVC นี่เป็นวิธีแก้ปัญหาที่เหมาะสมในการปฏิบัติตามวิธีการสร้างกรอบงาน 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>

แก้ไข:

เนื่องจากคุณมักจะนำเมนูของคุณมาใช้ซ้ำในหลาย ๆ หน้าจึงเป็นการดีที่จะมีวิธีใช้คลาสที่เลือกนั้นโดยอัตโนมัติตามหน้าปัจจุบันแทนที่จะคัดลอกเมนูหลาย ๆ ครั้งและทำด้วยตนเอง

วิธีที่ง่ายที่สุดคือใช้ค่าที่อยู่ในViewContext.RouteDataนั้นคือค่าActionและ Controllerเราสามารถต่อยอดจากสิ่งที่คุณมีอยู่ในปัจจุบันได้ดังนี้:

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

รหัสไม่สวย แต่จะทำงานให้เสร็จและให้คุณแยกเมนูออกเป็นบางส่วนได้หากต้องการ มีหลายวิธีในการทำสิ่งนี้ด้วยวิธีที่สะอาดกว่านี้ แต่เนื่องจากคุณเพิ่งเริ่มต้นฉันจะปล่อยไว้อย่างนั้น ขอให้โชคดีในการเรียนรู้ ASP.NET MVC!


แก้ไขล่าช้า:

คำถามนี้ดูเหมือนจะได้รับการเข้าชมเล็กน้อยดังนั้นฉันคิดว่าฉันจะใช้วิธีแก้ปัญหาที่หรูหรากว่านี้โดยใช้HtmlHelperส่วนขยาย

แก้ไข 03-24-2015: ต้องเขียนวิธีนี้ใหม่เพื่ออนุญาตให้มีการดำเนินการหลายอย่างและตัวควบคุมที่ทริกเกอร์พฤติกรรมที่เลือกรวมถึงการจัดการเมื่อเมธอดถูกเรียกจากมุมมองบางส่วนของการดำเนินการย่อยคิดว่าฉันจะแชร์การอัปเดต!

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

ทำงานร่วมกับ. 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;
}

ตัวอย่างการใช้งาน:

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

ขอบคุณที่ฉันพูดถึงนี่เป็นวิธีที่ฉันมีในตอนแรก แต่ฉันหวังว่าจะมีวิธีการเดียวตลอดทั้งรหัสของฉันในการเปลี่ยนคลาส "แอ็คทีฟ" ระหว่างอ็อบเจ็กต์ที่เลือก
Gillespie

2
@Gillespie ไม่ต้องห่วง! ฉันได้แก้ไขคำตอบเพื่อเพิ่มข้อมูลบางอย่างที่อาจช่วยคุณได้อีกเล็กน้อย
Dom

5
เกี่ยวกับการแก้ไขครั้งแรกมันใช้งานได้จริงสำหรับหน้าดัชนีเท่านั้น ดูเหมือนว่าเหตุผลคือเมื่อทำการเปรียบเทียบมันล้มเหลวเนื่องจาก ViewContext.RouteData.Values ​​["Action"] ไม่ส่งคืนอ็อบเจ็กต์ประเภท String (ยังไม่รู้ว่ามันทำงานอย่างไรสำหรับหน้าแรก .... ) . เมื่อฉันเปลี่ยน ViewContext.RouteData.Values ​​["Action"] ทั้งหมดเป็น ViewContext.RouteData.Values ​​["Action"] toString () มันใช้ได้กับทุกเพจ อาจต้องการเพิ่มสิ่งนี้ในโซลูชันของคุณในกรณีที่มีคนประสบปัญหาเดียวกัน
user3281466

2
@LonelyPixel คำตอบนี้เหมาะกับคุณหรือไม่?
Dom

1
สำหรับผู้ใช้ VB.NET: <li class = "@ (If (ViewContext.RouteData.Values ​​(" Action "). ToString () =" Index "," active "," "))"> @ Html.ActionLink (" Home "," Index "," Home ") </li>
Andrea Antonangeli

28

ส่วนขยาย:

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

การใช้งาน:

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

1
นี่คือส่วนขยายที่มีประสิทธิภาพอย่างสวยงาม ทำงานได้อย่างสมบูรณ์แบบและค่อนข้างหรูหรา ปิดให้คุณศ! ด้ามจับเดียวของฉันอยู่กับrole="presentation"ซึ่งไม่เหมาะสำหรับเมนูการนำทางหลัก ( john.foliot.ca/aria-hidden/#pr ) รายการที่ไม่เรียงลำดับเป็นคอนเทนเนอร์ที่เหมาะสมอย่างมากสำหรับชุดลิงก์ที่เกี่ยวข้องเนื่องจากจะรวมกลุ่มเข้าด้วยกันในลักษณะที่เป็นเหตุเป็นผล
RenéKåbis

@ RenéKåbisสำหรับ bootstrap naigation getbootstrap.com/components/#nav-tabs

1
สิ่งนี้ใช้ไม่ได้เมื่อผู้ใช้ป้อน url ด้วยตัวอักษรตัวพิมพ์เล็กหรือตัวพิมพ์ใหญ่เช่น " domain.com/Login " และ " domain.com/LOGIN " สำหรับแนวทางแก้ไข var str = String.Format("<li role=\"presentation\"{0}>{1}</li>", currentAction.toLower().Equals(action.toLower(), StringComparison.InvariantCulture) && currentController.toLower().Equals(controller.toLower(), StringComparison.InvariantCulture) ? " class=\"active\"" : String.Empty, html.ActionLink(text, action, controller).ToHtmlString() );
sky91

20

ฉันสับสนที่จะทำสิ่งนี้โดยการเพิ่มพารามิเตอร์ view bag ใน asp.net mvc ฉันทำอะไรลงไป

เพิ่มViewBag.Current = "Scheduler";พารามิเตอร์ like ในแต่ละหน้า

ในหน้าเค้าโครง

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

สิ่งนี้ช่วยแก้ปัญหาของฉันได้


วิธีแก้ปัญหาที่ง่ายและสะดวก
japes Sophey

13

อาจจะช้าไปหน่อย แต่หวังว่านี่จะช่วยได้

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

และการใช้งานดังนี้

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

ได้รับการอ้างอิงจากhttp://www.codingeverything.com/2014/05/mvcbootstrapactivenavbar.html


1
โซลูชันที่เรียบง่ายและสง่างามมาก ฉันได้ปรับปรุงบางอย่างที่นี่gist.github.com/jean-rakotozafy/…เพื่อเน้นเมนูสำหรับการทำงานของคอนโทรลเลอร์ทั้งหมด
Zan RAKOTO

5

ฉันรู้ว่าคำถามนี้เก่า แต่ฉันอยากจะเพิ่มเสียงของฉันที่นี่ ฉันเชื่อว่าเป็นความคิดที่ดีที่จะฝากความรู้ไว้ว่าลิงก์นั้นใช้งานได้กับคอนโทรลเลอร์ของมุมมองหรือไม่

ฉันจะกำหนดค่าที่ไม่ซ้ำกันสำหรับแต่ละมุมมองในการทำงานของคอนโทรลเลอร์ ตัวอย่างเช่นหากฉันต้องการให้ลิงก์โฮมเพจใช้งานได้ฉันจะทำสิ่งนี้:

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

จากนั้นในมุมมองของฉันฉันจะเขียนสิ่งนี้:

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

เมื่อคุณไปที่หน้าอื่นให้พูดว่า Programs, ViewBag ไม่มี Home (ViewBag.Programs แทน); ดังนั้นจึงไม่มีการแสดงผลไม่มีแม้แต่ class = "" ฉันคิดว่านี่สะอาดกว่าทั้งในเรื่องการดูแลรักษาและความสะอาด ฉันมักจะอยากละตรรกะออกไปให้มากที่สุดเท่าที่จะทำได้


4

เมื่อพิจารณาถึงสิ่งที่ Damith โพสต์ฉันชอบที่จะคิดว่าคุณสามารถมีสิทธิ์ใช้งานได้โดยViewbag.Title (แนวทางปฏิบัติที่ดีที่สุดคือเติมข้อมูลนี้ในหน้าเนื้อหาของคุณ_Layout.cshtmlเพื่อให้หน้าของคุณสามารถเก็บแถบลิงก์ของคุณได้) โปรดทราบว่าหากคุณใช้รายการเมนูย่อยมันก็ใช้ได้ดีเช่นกัน:

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

สะอาดและมีประสิทธิภาพ!
mggluscevic

3

ผมปรับเปลี่ยนของ Dom "ไม่สวย" คำตอบและทำให้มันไม่สวยงามเท่า บางครั้งตัวควบคุมสองตัวมีชื่อการกระทำที่ขัดแย้งกัน (เช่น Index) ดังนั้นฉันจึงทำสิ่งนี้:

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

3

คุณสามารถลองสิ่งนี้: ในกรณีของฉันฉันกำลังโหลดเมนูจากฐานข้อมูลตามการเข้าถึงตามบทบาทให้เขียนรหัสในทุกมุมมองของคุณว่าเมนูใดที่คุณต้องการใช้งานตามมุมมองของคุณ

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

3

วิธีนี้ง่ายสำหรับ Asp.net MCV 5

  1. Utilitarios.csสร้างระดับคงที่เช่น

  2. ภายในชั้นเรียนสร้างวิธีการคงที่:

    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. เรียกแบบนี้

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

3

ASP.NET Core และ Bootstrap 4

คำตอบที่ทันสมัยที่สุด

ฉันได้แก้ไขโซลูชันเรียบร้อยของ@crushอีกครั้งสำหรับวิธีที่เข้ากันได้กับASP.NET CoreและBootstrap 4 ที่อัปเดตแล้วเพื่อแก้ปัญหานี้โดยใช้IHtmlHelperวิธีการขยาย:

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

การใช้

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

1
ทางออกที่ดีมาก ในตัวอย่างของคุณฉันจะตั้งค่า "ผู้ติดต่อ" เป็นใช้งานโดยค่าเริ่มต้นได้อย่างไร
Md. Suman Kabir

1
ขอบคุณ. สิ่งที่ดีคือคุณไม่จำเป็นต้องตั้งค่าองค์ประกอบที่ใช้งานอยู่ ทันทีที่เข้าสู่หน้า 'ติดต่อ' ActiveActionLink()จะใช้คลาสที่ใช้งานอยู่กับ<li>:-)
WoIIe

คุณตรวจสอบพื้นที่ไหน
Mohammad

@ โมฮัมหมัดฉันไม่เข้าใจ คุณช่วยอธิบายคำถามของคุณให้ละเอียดได้ไหม
WoIIe

ในคลาส LinkExtensions คุณไม่ได้รับพื้นที่จาก routeData!
Mohammad

3

ASP.NET Core 3.0 และ TagHelpers ที่ง่ายดาย

[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");
            }
        }
    }
}

รวมไว้ใน _ViewImports.cs ของคุณ:

@addTagHelper *, YourAssemblyName

การใช้งาน:

 <li active-when="/Home/Index">

2

เพิ่ม ".ToString" เพื่อปรับปรุงเปรียบเทียบกับ 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>

-


สิ่งนี้จะใช้ไม่ได้หากมีชื่อการดำเนินการเดียวกันในตัวควบคุมสองตัวที่ต่างกัน ทดสอบกับคอนโทรลเลอร์ Home และ About ด้วย Index action
hyde

คุณสามารถกำหนดเซิร์ฟเวอร์ฟังก์ชันบนมีดโกนเช่น: @functions { public bool IsActive(string action, string controller) { var actionRoute = ViewContext.RouteData.Values["Action"].ToString(); var controlRoute = ViewContext.RouteData.Values["Controller"].ToString(); return controller.Equals(controlRoute)&& actionRoute.Equals(actionRoute); } }และการใช้งาน: <li class="@(IsActive("Index", "Home")? "active": "")">
Hua Trung

2

นอกจากนี้เรายังสามารถสร้างUrlHelperจาก RequestContext ซึ่งเราสามารถรับได้จาก MvcHandler เอง ดังนั้นฉันจึงเชื่อสำหรับคนที่ต้องการเก็บตรรกะนี้ไว้ในเทมเพลต Razor วิธีต่อไปนี้จะเป็นประโยชน์:

  1. ในโปรเจ็กต์รูทสร้างโฟลเดอร์ชื่อAppCode.
  2. สร้างไฟล์ชื่อ HtmlHelpers.cshtml
  3. สร้างผู้ช่วยที่นั่น:

    @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. จากนั้นเราสามารถใช้กับมุมมองของเราดังนี้:

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

หวังว่าจะช่วยให้ใครบางคน


นี่มันเจ๋งมาก มีข้อเสียด้านประสิทธิภาพหรือไม่?
ขยี้

@crush ผมยังไม่ได้วัด แต่ฉันจะคาดหวังว่ามันจะมีผลกระทบต่อประสิทธิภาพต่ำเพราะผมคิดว่ากรอบการใช้ตรรกะที่คล้ายกันในการสร้างที่คุณมีในUrlHelper Controller.Urlสิ่งเดียวคือเราอาจไม่ต้องการดำเนินการสร้างUrlHelperสำหรับการเรียกแต่ละครั้งMenuItemดังนั้นเราอาจใช้เครื่องหมายในการบันทึกลงในรายการ HttpContext หลังจากเรียกไปยังผู้ช่วยเหลือครั้งแรกดังนั้นเราจึงทำเพียงครั้งเดียวต่อคำขอ
Oleksii Aza

2

เป็นไปได้ด้วยฟังก์ชันแลมบ์ดา

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

แล้วการใช้งาน

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

2

ฉันสร้างHtmlHelperส่วนขยายที่เพิ่มActiveActionLinkเมธอดสำหรับพวกคุณที่ต้องการเพิ่มคลาส "แอ็คทีฟ" ลงในลิงก์เองแทนที่จะอยู่<li>รอบ ๆ ลิงก์


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

การใช้งานมีดังนี้:

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

1

คำตอบโดย @dombenoit ใช้งานได้ แม้ว่าจะแนะนำรหัสบางอย่างเพื่อรักษา ตรวจสอบไวยากรณ์นี้:

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", "#")
}

สังเกตการใช้.SetLinksActiveByControllerAndAction()วิธีการ

หากคุณสงสัยว่าอะไรทำให้ไวยากรณ์นี้เป็นไปได้ลองดูTwitterBootstrapMVC


ขอบคุณสำหรับคำตอบ Dmitry ฉันกำลังทดสอบ TwitterBootstrapMVC
Gillespie

1

ฉันยังมองหาวิธีแก้ปัญหาและ jQuery ช่วยได้มาก ขั้นแรกคุณต้องให้ 'id ของ<li>องค์ประกอบของคุณ

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

หลังจากทำสิ่งนี้ในหน้าเค้าโครงของคุณคุณสามารถบอก jQuery ว่า<li>ควรจะ 'เลือกองค์ประกอบใด' ในมุมมองของคุณ

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

หมายเหตุ: คุณต้องมี@RenderSection("scripts", required: false)ในหน้าเค้าโครงก่อน</body>แท็กเพื่อเพิ่มส่วนนั้น


1

ฉันต้องการเสนอวิธีแก้ปัญหานี้ซึ่งอ้างอิงจากส่วนแรกของคำตอบของ Dom

ก่อนอื่นเรากำหนดตัวแปรสองตัวคือ "การกระทำ" และ "ตัวควบคุม" และใช้ตัวแปรเหล่านี้เพื่อกำหนดลิงก์ที่ใช้งานอยู่:

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

แล้ว:

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

ตอนนี้มันดูดีขึ้นและไม่จำเป็นต้องมีโซลูชันที่ซับซ้อนมากขึ้น


0

ถ้ามันไม่แสดงเลยเหตุผลก็คือคุณต้องมีเครื่องหมาย @ สองตัว:

@@class

แต่ฉันเชื่อว่าคุณอาจต้องมีคลาสที่ใช้งานอยู่บนแท็ก "li" ไม่ใช่ในแท็ก "a" ตามเอกสาร bootstrap มากเกินไป ( 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>

ดังนั้นรหัสของคุณจะเป็น:

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

คลาส @@ แสดงข้อผิดพลาดเดิมทีฉันมีตัวอย่างโค้ดของคุณด้านบนซึ่งใช้งานได้ แต่ไม่ตรงตามที่ Actionlinks "ควร"?
Gillespie

แก้ไข ไม่จำเป็นต้องใช้ @@ คุณต้องเข้าเรียนใน EA
JC Lizard

0

หวังว่านี่จะช่วยได้ด้านล่างนี้คือวิธีที่เมนูของคุณสามารถ:

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

และเพิ่มฟังก์ชันตัวช่วย MVC ด้านล่างแท็ก 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;
        }
    }

ฉันอ้างอิงคำตอบจากhttp://questionbox.in/add-active-class-menu-link-html-actionlink-asp-net-mvc/


0

ฉันตระหนักว่าปัญหานี้เป็นปัญหาทั่วไปสำหรับพวกเราบางคนดังนั้นฉันจึงเผยแพร่วิธีแก้ปัญหาของตัวเองโดยใช้แพ็คเกจ nuget ด้านล่างนี้คุณสามารถดูวิธีการทำงาน ฉันหวังว่าจะเป็นประโยชน์

หมายเหตุ: แพ็คเกจ nuget นี้เป็นแพ็คเกจแรกของฉัน ดังนั้นหากคุณเห็นข้อผิดพลาดโปรดให้ข้อเสนอแนะ ขอบคุณ.

  1. ติดตั้งแพ็คเกจหรือดาวน์โหลดซอร์สโค้ดและเพิ่มโครงการของคุณ

    -Install-Package Betalgo.MvcMenuNavigator
  2. เพิ่มเพจของคุณลงใน enum

    public enum HeaderTop
    {
        Dashboard,
        Product
    }
    public enum HeaderSub
    {
        Index
    }
  3. วางตัวกรองไว้ที่ด้านบนของตัวควบคุมหรือการดำเนินการของคุณ

    [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. และใช้งานได้ในรูปแบบส่วนหัวของคุณเช่นนี้

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

ข้อมูลเพิ่มเติม: https://github.com/betalgo/MvcMenuNavigator


0

ฉันเชื่อว่านี่เป็นรหัสที่สะอาดและเล็กกว่าในการทำให้เมนูที่เลือก "ใช้งานอยู่":

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

ดีแม้ว่าอาจต้องขยายสำหรับคอนโทรลเลอร์และพื้นที่ในบางกรณี
planetClaire

-1

ฉันได้รวบรวมคำตอบข้างต้นและทำการแก้ปัญหาของฉัน

ดังนั้น..

อันดับแรกในบล็อกมีดโกนสร้างตัวแปรสตริงหนึ่งตัวซึ่งจะมีค่าชื่อของตัวควบคุมและการดำเนินการที่เรียกโดยผู้ใช้

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

จากนั้นใช้โค้ด HTML และ 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>

ฉันคิดว่านี่เป็นสิ่งที่ดีเพราะคุณไม่จำเป็นต้องเข้าถึง "ViewContext.RouteData.Values" ทุกครั้งเพื่อรับชื่อคอนโทรลเลอร์และชื่อการดำเนินการ


-1

ทางออกของฉันสำหรับปัญหานี้คือ

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

วิธีที่ดีกว่าอาจเพิ่มวิธีการขยาย Html เพื่อส่งคืนเส้นทางปัจจุบันเพื่อเปรียบเทียบกับลิงก์


-1

ฉันทำอย่างนี้:

สร้างตัวช่วยในมุมมองเมนูบางส่วน

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

จากนั้นใช้ในแท็กจุดยึดดังนี้:

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

แอปพลิเคชันของฉันไม่มีพื้นที่ แต่สามารถรวมเป็นตัวแปรอื่นในฟังก์ชันตัวช่วยได้ และฉันต้องส่งคลาสที่ใช้งานไปยังแท็กจุดยึดในมุมมองของฉัน แต่liยังสามารถกำหนดค่าเช่นนี้


1
คุณจะเห็นว่าการโหวตลดลงไม่ได้ช่วยอะไรเลยถ้าคุณไม่พูดถึงเหตุผล
Anup Sharma
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.