103

Code below doesn't seems clean. Any suggestion to improve the code?

<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> 
1
  • Perhaps creating a custom HTML helper that would render out the LI with child link elements? Commented Feb 22, 2012 at 17:09

8 Answers 8

182

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

Sign up to request clarification or add additional context in comments.

5 Comments

Be careful that your class name isn't 'active' or you'll have an entry class attribute. No idea why.
Is there a way to achieve same null-respecting behaviour when calling to html helpers passing the anonymous htmlProperties object? E.g. I want to conditionally pass attribute disabled, like @Html.TextBoxFor(lambda, new { disabled = condition ? true : null }), but that still renders disabled="" when disabled was null, which is the same as rendring disabled="anything" because disabled is active when the attribute is present, regardless of the value. Found stackoverflow.com/q/7978137/11683 on the topic, but are there better ways nowadays I wonder?
Side note: "data-..." attributes can't be conditional and render empty value even for null (stackoverflow.com/questions/13267619/…)
Damn! Didn't know this until now :-)
@GSerg Try using @Html.TextBoxFor(lambda, condition ? new { disabled = true } : null); that's what I typically do for HTML Helpers. The caveat is I believe it has to either be an object or null to work (ie. you can't use two anonymous objects with one property removed as one result).
85
<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.

2 Comments

Nice one, much cleaner than some of the other options (besides the Razor 2.0 option).
@ctrlplusb mind telling me about this Razor 2.0 option you're talking about?
25

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.

2 Comments

Very elegant way for do that. But instead of Func<bool> lambda, i will prefer a simple bool? parameter, because it's simpler: <li @Html.Attr("class", "new", example.isNew)>
I like this approach because a lot of the custom JavaScript in my app will still run when the attribute name is still there. And at least this doesn't make you repeat the same div because of attribute differences. Thanks!
8
@{ var classAttr= needClass ? "class=\"class-name\"" : "" }

and then in the HTML

<li @Html.Raw(classAttr) >  

Comments

5

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/

Comments

3

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

Comments

2

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

Comments

1

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.