.NET
ASP.NET MVC: Aktuellen Menülink hervorheben
Posted on .Dieses „Problem“ müsste eigentlich jeder kennen der schon mein eine Webseite mit mehreren Bereichen erstellt hat. Wie kann ich den Menüpunkt der aktuell angezeigten Seite hervorheben?
Hier gibt es viele Ansätze. Ich hatte schon Seiten, die die CSS Klassen ins HTML hardcoden, irgendwelche anderen Schandtaten betreiben oder nicht wirklich schöne If-Abfragen um die Menüpunkte legen.
<li @if (activeMenu.Equals("Home"))
{
<text>class="active"</text>
}>@Html.ActionLink("Home", "index", "home")</li>
Hier muss man natürlich noch bedenken, dass ein entsprechender ViewBag-Eintrag namens „activeMenu“ in jeder Action gesetzt werden muss. urgs
Ich möchte diese ganze Logik nicht in den Views haben. Daher habe ich eine kleine ExtensionMethot für den HtmlHelper geschrieben.
Diese Extension generiert für mich die Menülinks und prüft beim Generieren des Links ob das Ziel des Links mit dem aktuellen Controller und der aktuellen View übereinstimmt. Wenn das der Fall ist wird dieser Link als aktiv markiert. So sieht das Ganze aus:
public static MvcHtmlString NavigationActionLink(
this HtmlHelper htmlHelper, string linkText,
string actionName, string controllerName, object routeValues = null,
object htmlAttributes = null,
string wrapperElement = "li", string flag = "active")
{
var generatedLink = HtmlHelper.GenerateLink(
htmlHelper.ViewContext.RequestContext,
htmlHelper.RouteCollection, linkText,
(string)null, actionName, controllerName,
new RouteValueDictionary(routeValues),
(IDictionary<string, object>)HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
var wrappedElement = WrapGeneratedLink(
htmlHelper, actionName,
controllerName, wrapperElement, flag, generatedLink);
return MvcHtmlString.Create(wrappedElement.ToString(TagRenderMode.Normal));
}
private static TagBuilder WrapGeneratedLink(
HtmlHelper htmlHelper, string actionName, string controllerName,
string wrapper, string flag, string generatedLink)
{
var wrapperElement = new TagBuilder(wrapper)
{
InnerHtml = generatedLink
};
if (CurrentRouteMatchesGeneratedUrl(actionName, controllerName, htmlHelper))
{
wrapperElement.AddCssClass(flag);
}
return wrapperElement;
}
private static bool CurrentRouteMatchesGeneratedUrl(
string actionName, string controllerName, HtmlHelper htmlHelper)
{
var currentAction = (string)htmlHelper.ViewContext.RouteData.Values["action"];
var currentController = (string)htmlHelper.ViewContext.RouteData.Values["controller"];
return string.Equals(currentAction, actionName,
StringComparison.CurrentCultureIgnoreCase) &&
string.Equals(currentController, controllerName,
StringComparison.CurrentCultureIgnoreCase);
}
Hier der Code nochmal etwas schöner formatiert.
Dieser Helper orientiert sich an dem gewöhnlichen ActionLink und hat die gleichen Parameter bis auf die letzten zwei, mit denen man bestimmen kann wie der Link hervorgehoben werden soll.
So könnte man nun die Menülinks verwenden am Beispiel Twitter Bootstrap:
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<div class="nav-collapse">
<ul class="nav">
@Html.NavigationActionLink("meinlink", "index", "home")
@Html.NavigationActionLink("meinlink", "index", "account", null, null, "div", "selected")
</ul>
</div>
</div>
</div>
</div>
So würde der erste Link folgendes generieren:
<li class="active"><a href="/">meinlink</a></li>
Und der zweite Link das hier:
Error: Page not found
The requested URL was not found on this server.
Auf diese Weise kann mann etwas Einfluss darauf nehmen was da generiert wird und mit welcher Klasse das erzeugte Feld markiert wird.
Der zweite Link macht natürlich keinen Sinn in dem Twitter Bootstrap Beispiel und wurde nur zu Demonstrationszwecken genutzt 🙂
So entfernt man den hässlichen Code aus den Views und muss sich darum keinen Kopf mehr machen.
Übrigens: In dem meisten Fällen reicht es wenn man nur nach dem Controller prüft, da häufig mehrere Actions zu einem Menüpunkt gehören und man dann einen Menüpunkt mit einem Controller abdeckt.
Wie macht ihr das?
Wie ist eure Methode um diese kleine Problematik zu lösen? Habt ihr vielleicht einen schöneren/einfacheren Ansatz? Dann würde ich mich sehr freuen wenn ihr mich belehren würdet 🙂
Sebastian Gross
http://www.bigbasti.comSebastian Gross arbeitet in Bielefeld als Softwareentwickler für .NET und Java im Bereich Web.Als Fan der .NET-Plattform lässt er sich kein Userguppen Treffen und Community Event im Raum OWL entgehen.Dabei hat er eine besondere Vorliebe für das ASP.NET MVC Framework und für das Test Driven Development (TDD) entwickelt.
Author Tobias
Posted at 20:42 8. Oktober 2012.
Solche HtmlHelper zu schreiben – wie du es machst, find ich eine super Idee.
Mein persönlicher Ansatz liegt jedoch am Client:
So kann ich den Tag und seine Attribute sowie Inhalte einfacher modifizieren, indem ich das Markup ausschreibe – der HtmlHelper ist hier ein absoluter Overkill für ein eigentlich einfaches Markup.
Link
Da das Highlighting keine Vorteile bringt, um vom Server generiert werden zu müssen, gehe ich alle nötigen Links bzw. Navigationen im Javascript durch, und kann mich hier anhand der aktuellen URL durch den ganzen Seitenbaum arbeiten.
Hier im Beispiel mit jQuery:
var loc = location.href.toLowerCase().split(‚#‘)[0];
var base = location.protocol + ‚//‘ + location.host + ‚/‘;
// add active to navigation link
$(‚a‘).each(function ()
{
if (loc.indexOf(this.href.toLowerCase()) > -1 && (this.href !== base || loc === base))
{
$(this).addClass(‚active‘);
}
});
Das ist ein kleines Schnipsel, könnte auch einfach in ein Plugin ausgelagert werden, und hält das code-behind sauber.
Drawbacks:
1. Mehr Last auf den Client
2. Funktioniert nur mit aktiviertem JS
Author Epstone
Posted at 16:30 25. Oktober 2012.
Danke, beides tolle Lösungen! Mein letzter Ansatz war deutlich umständlicher.