NET MVC 剃刀: HTML 中的条件属性

下面的代码看起来不干净。 有什么改进代码的建议吗?

<li @if(ViewData["pagename"].ToString()=="Business details"){ <text>class="active" </text> } >
<a  @if(ViewData["pagename"].ToString()=="Business details"){ <text>style="color: white; background-color: #08C; border: 1px solid #08C;" </text> }
href="@Url.Action("BusinessDetails", "Business")">Business Details</a>
</li>
<li @if (ViewData["pagename"].ToString() == "Booking policies"){ <text>class="active"</text> }>
<a  @if (ViewData["pagename"].ToString() == "Booking policies")
{ <text>style="color: white; background-color: #08C; border: 1px solid #08C;" </text> }
href="@Url.Action("BookingPolicies", "Business")">Booking policies</a>
</li>
124238 次浏览
<li class="@(ViewBag.pagename == "Business details" ? "active" : null)">

You should replace the inline style="..." with a separate classname and use the same syntax there.

However, it would be cleaner to make a separate HTML helper extension method that takes a page and action name and generates the HTML generically.

MVC has conditional attributes built in. For example:

<div @{if (myClass != null) { <text>class="@myClass"</text> } }>Content</div>
<div class="@myClass">Content</div>

If @myClass is null, it just won't use the attribute at all.

I know that may not quite solve your current issue, but it is noteworthy!

See also: John Galloway's blog post on ASP.NET MVC 4 Beta > Conditional attribute rendering

In MVC4

<!DOCTYPE html>
<html>
<head>
</head>
<body>
@{
string css = "myDiv";
}
<div class='@css'></div>
</body>
</html>

or

<!DOCTYPE html>
<html>
<head>
</head>
<body>
@{
string css = "class=myDiv";
}
<div @css></div>
</body>
</html>

More are here: http://evolpin.wordpress.com/2012/05/20/mvc-4-code-enhancements/

I use a small helper method that will conditionally add an attribute if the value is non-empty, and, if defined, when a Boolean function expression evaluates to true:

public static MvcHtmlString Attr(this HtmlHelper helper, string name, string value, Func<bool> condition = null)
{
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value))
{
return MvcHtmlString.Empty;
}


var render = condition != null ? condition() : true;


return render ?
new MvcHtmlString(string.Format("{0}=\"{1}\"", name, HttpUtility.HtmlAttributeEncode(value))) :
MvcHtmlString.Empty;
}

Once defined, I can use this method in my Razor views:

<li @(Html.Attr("class", "new", () => example.isNew))>
...
</li>

The above code will render <li class="new">...</li> if example.isNew == true, if not will omit the entire class attribute.

Approach with TagWrap extension method. Code for your question would look like this:

@using (Html.TagWrap("li", condition ? new { @class = "active" } : null))
{
var anchorAttrs = new Dictionary<string, object> { { "href", Url.Action("BusinessDetails", "Business") } };
if(condition)
{
anchorAttrs["style"] = "color: white; background-color: #08C; border: 1px solid #08C;";
}
using (Html.TagWrap("a", anchorAttrs))
{
<text>Business Details</text>
}
}

TagWrap extension methods

using Microsoft.AspNetCore.Mvc.ViewFeatures;

public static IDisposable TagWrap(this IHtmlHelper htmlHelper, string tagName, object data)
{
return htmlHelper.TagWrap(tagName, HtmlHelper.AnonymousObjectToHtmlAttributes(data));
}


public static IDisposable TagWrap(this IHtmlHelper htmlHelper, string tagName, IDictionary<string, object> data)
{
var tag = new TagBuilder(tagName);
tag.MergeAttributes(data);


htmlHelper.ViewContext.Writer.Write(tag.RenderStartTag());


return new DisposableAction(() =>
htmlHelper.ViewContext.Writer.Write(tag.RenderEndTag()));
}

Helper class used for rendering closing tag on Dispose

public class DisposableAction : IDisposable
{
private readonly Action DisposeAction;


public DisposableAction(Action action)
{
DisposeAction = action;
}


public void Dispose()
{
DisposeAction();
}
}

Based on defrosts answer here an adaptation, taking an object instead of a string:

    public static MvcHtmlString ConditionalAttr(this HtmlHelper helper, string attributeName, object value, Func<bool> condition)
{
if (string.IsNullOrEmpty(attributeName) || value == null)
{
return MvcHtmlString.Empty;
}


var render = condition != null ? condition() : true;


return render ?
new MvcHtmlString($"{attributeName}=\"{HttpUtility.HtmlAttributeEncode(value.ToString())}\"") :
MvcHtmlString.Empty;
}

This way you don't have to turn your other datatypes in strings before passing them, saving a fiew .ToString(). There is a difference tho: passing an empty string will still render. As example:

@Html.ConditionalAttr("data-foo", "", () => Model.IsFooNeeded)


// Ouput:
data-foo=""
@{ var classAttr= needClass ? "class=\"class-name\"" : "" }

and then in the HTML

<li @Html.Raw(classAttr) >

This might be the shortest way, I tested it for .NET Core 6 but I guess it should work in general:

<div @(condition ? (" class=" + @className) : null) />