44

I'm looking for a way to write the following code with less lines of code (maybe 5). I suppose I could do the same thing as the selected class but this razor syntax isn't looking pretty.

<ul>
@foreach (var mi in Model.MenuItems) {
  <li@(mi.Selected?" class=\"selected\"":null)>
  @if (string.IsNullOrEmpty(mi.Title)) {
    <a href="@mi.Href">@mi.Text</a>
  } else {
    <a href="@mi.Href" title="@mi.Title">@mi.Text</a>
  }
  </li>
}
</ul>
1
  • 2
    Latest version of razor now has built in support for this. See answer below. Commented Apr 17, 2012 at 15:53

8 Answers 8

60

Fixed in ASP.NET MVC 4

see http://weblogs.asp.net/jgalloway/archive/2012/02/16/asp-net-4-beta-released.aspx

Conditional attribute rendering

If you have an attribute that might be null, in the past you've needed to do a null check to avoid writing out an empty attribute, like this:

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

Now Razor is able to handle that automatically, so you can just write out the attribute. If it's null, the attribute isn't written:

<div class="@myClass">Content</div>

So if @myClass is null, the output is just this:

<div>Content</div>
Sign up to request clarification or add additional context in comments.

4 Comments

You don't actually mean ASP.NET 4, you mean ASP.NET MVC 4 which at the time of writing is still in beta - ASP.NET 4 shipped with VS2010 and .NET 4.0 and does not contain conditional attribute rendering.
How do I do that if I want to add a style to other styles. Say if a bool is true then display:none for example. Or do I still need to use that ugly conditional?
@ppumkin I did it like this; var trstyle = index == 0 ? "display: none;" : null;, then in the tr itself I do <tr style="@trstyle">, which makes the first row hidden (index == 0). The output is simply <tr> for the other rows. Worked beautifully. +1.
This does not work for data-xxx attributes, though. Aaargh.... Inconsistencies....
41

I've come up with a chainable HtmlAttribute class and some Html Extension methods to allow the Razor syntax below:

<ul> 
    @foreach (var mi in items) { 
    <li @Html.Css("selected", mi.Selected)> 
        <a href="@mi.Href" @Html.Attr("title", mi.Title)>@mi.Text</a> 
    </li> 
    } 
</ul> 

Here is the HtmlAttribute class:

public class HtmlAttribute : IHtmlString     
{
    private string _InternalValue = String.Empty;
    private string _Seperator;

    public string Name { get; set; }
    public string Value { get; set; }
    public bool Condition { get; set; }

    public HtmlAttribute(string name)
        : this(name, null)
    {
    }

    public HtmlAttribute( string name, string seperator )
    {
        Name = name;
        _Seperator = seperator ?? " ";
    }

    public HtmlAttribute Add(string value)
    {
        return Add(value, true);
    }

    public HtmlAttribute Add(string value, bool condition)
    {
        if (!String.IsNullOrWhiteSpace(value) && condition)
            _InternalValue += value + _Seperator;

        return this;
    }

    public string ToHtmlString()
    {
        if (!String.IsNullOrWhiteSpace(_InternalValue))
            _InternalValue = String.Format("{0}=\"{1}\"", Name, _InternalValue.Substring(0, _InternalValue.Length - _Seperator.Length));
        return _InternalValue;
    }
}

Extra info: The "seperator" is used to chain together multiple values for an attribute. This can be useful for multiple css class names (use a space) or perhaps use String.Empty to build an value dependant on multiple conditions (by using the .Add() method)

And here are the Html Extension helper methods:

public static class Extensions
{
    public static HtmlAttribute Css(this HtmlHelper html, string value)
    {
        return Css(html, value, true);
    }

    public static HtmlAttribute Css(this HtmlHelper html, string value, bool condition)
    {
        return Css(html, null, value, condition);
    }

    public static HtmlAttribute Css(this HtmlHelper html, string seperator, string value, bool condition)
    {
        return new HtmlAttribute("class", seperator).Add(value, condition);
    }

    public static HtmlAttribute Attr(this HtmlHelper html, string name, string value)
    {
        return Attr(html, name, value, true);
    }

    public static HtmlAttribute Attr(this HtmlHelper html, string name, string value, bool condition)
    {
        return Attr(html, name, null, value, condition);
    }

    public static HtmlAttribute Attr(this HtmlHelper html, string name, string seperator, string value, bool condition)
    {
        return new HtmlAttribute(name, seperator).Add(value, condition);
    }
}

Let me know if they are of use.

Thanks,

Lee

2 Comments

You need to escape the values.
Hi Lee, can you give me a clue on where to put these classes in an MVC project, and how/where to reference them?
12
<ul>
@foreach (var mi in Model.MenuItems) {
    <li@(mi.Selected?" class=\"selected\"":null)>
        <a href="@mi.Href" @{if(!string.IsNullOrEmpty(mi.Title)) { <text>title="@mi.Title"</text>} }>@mi.Text</a>
    </li>
}
</ul>

I haven't tested it but it parses correctly.

3 Comments

Wow, had you taken the time too look at the error message you would have seen that all you needed to do was add a {} around the if block contents and you would have seen my code produces the exact output you were looking for...
Yikes, I did "look" at the error message and tried the brackets but I must not have comprehended the message. :) Thanks for updating your post.
Sorry, I know it came off as rude. Bad morning. That said, I would definitely look at Darin's html helper since that was MVC is basically all about.
7

That would be a good candidate for custom HTML helper:

public static class HtmlExtensions
{
    public static MvcHtmlString MenuItem(this HtmlHelper htmlHelper, MenuItem mi)
    {
        var li = new TagBuilder("li");
        if (mi.Selected)
        {
            li.AddCssClass("selected");
        }
        var a = new TagBuilder("a");
        a.MergeAttribute("href", mi.Href);
        if (!string.IsNullOrEmpty(mi.Title))
        {
            a.MergeAttribute("title", mi.Title);
        }
        a.SetInnerText(mi.Text);
        return MvcHtmlString.Create(li.ToString());
    }
}

and in your view:

<ul>
@foreach (var mi in Model.MenuItems) {
    @Html.MenuItem(mi)
}
</ul>

or using DisplayTemplates you don't even need to write a loop:

<ul>
    @Html.DisplayFor(x => x.MenuItems)
</ul>

1 Comment

Darin, your code is great but somehow I feel like this is defeating the purpose of razor. Microsoft needs to address this.
5
<ul>
@foreach (var mi in Model.MenuItems) {
  <li@(Html.Raw((mi.Selected ? " class=\"selected\"" : null))>
    <a href="@mi.Href">@mi.Text</a>
  </li>
}
</ul>

Comments

4

class attribute would not be rendered by Razor if value is null

<a href="#nolink" class="@(categoryId == null ? "submenu-active": null)">All</a>

Comments

1

For the case of multiple classes I use this simple extension method:

public static MvcHtmlString If(this string text, bool condition) {
    return new MvcHtmlString(condition ? text : string.Empty);
}

And in the view:

<div class="menuitem @("active".If(Model.Active))">

Comments

-1

It's really pretty simple and clean:

<p @(cssClass != null) ? { class="@cssClass" }> Stuff and whatnot... </p>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.