111

The variable strCSSClass often has a value but sometimes is empty.

I do not want to include an empty class="" in this input element's HTML, which means if strCSSClass is empty, I don't want the class= attribute at all.

The following is one way to do a conditional HTML attribute:

<input type="text" id="@strElementID" @(CSSClass.IsEmpty() ? "" : "class=" + strCSSClass) />

Is there a more elegant way of doing this? Specifically one where I could follow the same syntax as is used in the other parts of the element: class="@strCSSClass" ?

KyleMit
  • 30,350
  • 66
  • 462
  • 664
tony722
  • 1,985
  • 2
  • 15
  • 14

3 Answers3

173

You didn't hear it from me, the PM for Razor, but in Razor 2 (Web Pages 2 and MVC 4) we'll have conditional attributes built into Razor (as of MVC 4 RC tested successfully), so you can write things like this:

<input type="text" id="@strElementID" class="@strCSSClass" />

If strCSSClass is null then the class attribute won't render at all.

Further Reading

KyleMit
  • 30,350
  • 66
  • 462
  • 664
Erik Porter
  • 2,286
  • 1
  • 14
  • 8
  • 1
    Yup, you definitely shouldn't accept mine as the answer. That said, today there is no cleaner way to do it (hence we're creating a cleaner way to do it). Probably the cleanest way to do it is to use Html.TextBox, but that has a different set of less desirable things to it. :( Glad you like what we're adding though. :) – Erik Porter Nov 16 '11 at 23:07
  • 1
    But how can i combine Razor attributes with other text? I need to make the following: ... id="track_@track.ID". I've expected something like ...id="track_2", but it generated the following output: ...id="track_@track.ID"... – Laserson Jan 15 '12 at 17:57
  • 2
    You can force Razor to evaluate code by putting parens around it. id="track_@(track.ID)" – Erik Porter Jan 27 '12 at 18:42
  • You mean to tell me that the entire 'class=""' attribute disappears? Hard to believe. Razor's parser does a lot more than I thought it did. – ATL_DEV May 23 '12 at 20:05
  • Only if you render something inside of the class attribute and if that something is a null value. Otherwise, it will still render. – Erik Porter May 25 '12 at 20:29
  • 1
    Added note indicating this works successfully with MVC 4. If you are on MVC 3 see my answer below. – AaronLS Nov 06 '12 at 20:29
  • I like the way it removes the spacing surrounding the attribute assignment if its not used. – Ian Warburton Jun 27 '13 at 11:12
  • 4
    @ErikPorter PLEASE DO NOT MESS WITH MY HTML!! also see http://stackoverflow.com/questions/9234467/razor-view-engine-automatically-applying-quotes/21126527#21126527 this as well for other bad behaviour – eaglestorm Jan 15 '14 at 00:04
  • 1
    @eaglestorm Have to a agree with you, IMHO there is too much magic going on here. There is a workaround for some of these cases `` will prevent razor from trying to add it's own quotes(I think Razors "magic" is partly a security feature so you have to use Raw to circumvent it) – AaronLS Mar 19 '14 at 23:23
  • @AaronLS Yup. Razor is meant to be fairly magical by default, but there should always be ways to avoid the magic when you want. Btw, I don't work at Microsoft anymore (as of about a year ago). :) – Erik Porter Mar 20 '14 at 01:49
  • 1
    @ErikPorter I've always liked Razor for it's lack of magic IMO that makes it easy to write a bunch of code and get expected results in a single pass, rather than lots of guess-check, that's relative to other past MS technologies. Hope you are enjoying your new endeavors. – AaronLS Mar 20 '14 at 20:07
  • @ErikPorter great. Please refer to [this](http://stackoverflow.com/questions/29090192) post, in addition to the ternary operator which is great, there should be a unary boolean operator that renders only if true. For example `` or anything with simpler/smarter/shorter syntax than that. – Shimmy Weitzhandler Mar 22 '15 at 00:06
  • This doesn't work for data attributes (and possibly any attribute it doesn't recognise). What you end up with is an attribute with no value. E.g. `` , so what you end up having to do is a conditional statement with 2 blocks of almost the same markup. – AaronHolland May 01 '18 at 01:51
  • This is false. I just tried it with `@disabled = null`, and I get a `disabled` attribute without value like `` – sergiol Mar 20 '23 at 13:41
139

Note you can do something like this(at least in MVC3):

<td align="left" @(isOddRow ? "class=TopBorder" : "style=border:0px") >

What I believed was razor adding quotes was actually the browser. As Rism pointed out when testing with MVC 4(I haven't tested with MVC 3 but I assume behavior hasn't changed), this actually produces class=TopBorder but browsers are able to parse this fine. The HTML parsers are somewhat forgiving on missing attribute quotes, but this can break if you have spaces or certain characters.

<td align="left" class="TopBorder" >

OR

<td align="left" style="border:0px" >

What goes wrong with providing your own quotes

If you try to use some of the usual C# conventions for nested quotes, you'll end up with more quotes than you bargained for because Razor is trying to safely escape them. For example:

<button type="button" @(true ? "style=\"border:0px\"" : string.Empty)>

This should evaluate to <button type="button" style="border:0px"> but Razor escapes all output from C# and thus produces:

style=&quot;border:0px&quot;

You will only see this if you view the response over the network. If you use an HTML inspector, often you are actually seeing the DOM, not the raw HTML. Browsers parse HTML into the DOM, and the after-parsing DOM representation already has some niceties applied. In this case the Browser sees there aren't quotes around the attribute value, adds them:

style="&quot;border:0px&quot;"

But in the DOM inspector HTML character codes display properly so you actually see:

style=""border:0px""

In Chrome, if you right-click and select Edit HTML, it switch back so you can see those nasty HTML character codes, making it clear you have real outer quotes, and HTML encoded inner quotes.

So the problem with trying to do the quoting yourself is Razor escapes these.

If you want complete control of quotes

Use Html.Raw to prevent quote escaping:

<td @Html.Raw( someBoolean ? "rel='tooltip' data-container='.drillDown a'" : "" )>

Renders as:

<td rel='tooltip' title='Drilldown' data-container='.drillDown a'>

The above is perfectly safe because I'm not outputting any HTML from a variable. The only variable involved is the ternary condition. However, beware that this last technique might expose you to certain security problems if building strings from user supplied data. E.g. if you built an attribute from data fields that originated from user supplied data, use of Html.Raw means that string could contain a premature ending of the attribute and tag, then begin a script tag that does something on behalf of the currently logged in user(possibly different than the logged in user). Maybe you have a page with a list of all users pictures and you are setting a tooltip to be the username of each person, and one users named himself '/><script>$.post('changepassword.php?password=123')</script> and now any other user who views this page has their password instantly changed to a password that the malicious user knows.

AaronLS
  • 37,329
  • 20
  • 143
  • 202
  • That is a very good point! And actually, it makes it readable and usable in most situations. – Dmytro Shevchenko May 01 '12 at 06:53
  • That's what I was doing in the example in my question. Thanks for a more elaborate explanation and example. :) – tony722 Jun 01 '12 at 00:23
  • 1
    Beware of spaces though. "style= display : none;" renders as none;="" :="" style="display" – wmcainsh Jan 30 '13 at 12:17
  • I think this is the exact solution for this problem +1 – Alberto León Jul 29 '13 at 18:11
  • This makes things much readable! I agree that this is the exact solution for this problem! – Frank Fu Sep 05 '13 at 04:26
  • This works great for MVC 3... can't wait to upgrade to mvc 4. – PJH Nov 14 '13 at 18:58
  • 1
    So why doesn't this work re: magic double quotes It just produces – rism Jan 29 '15 at 00:19
  • @AaronLS Hmm not for me when I run it I get no auto-supplied double quotes. – rism Jan 29 '15 at 02:57
  • @AaronLS When running the fiddle by clicking your link and doing a view source. I get via view-source:https://dotnetfiddle.net/MvcPage/9bd10c2dc2a347be85057732abfb2237 – rism Jan 29 '15 at 03:30
  • @rism One button should be grey with rounded borders, and the other green with square corners. I suspect your browser's HTML inspector or some extension is messing with the display of the HTML. – AaronLS Jan 29 '15 at 03:41
  • 1
    @AaronLS Yes that's exactly how they are. Cant understand how the browser (Chrome 40/FF33.1/IE 10) would affect anything since this is server generated markup and if so how come only those two class attributes but not for the class attribute of the ask button or even the type="button" attributes of all three buttons. Definitely a server thing IMHO since I can also RDP into a couple of Azure virtual machines dotted around the globe for the same result IE/Firefox/Chrome. – rism Jan 29 '15 at 03:50
  • @rism I switched to the network tab to see exactly what was returned over the wire, and indeed I can see what you've described, the missing quotes. So chrome must be fixing the missing quotes when parsing the HTML into the DOM. – AaronLS Jan 29 '15 at 15:47
  • @rism Thanks rism, after discovering this, I remembered some nuances of how Chrome handles displaying character codes in the HTML inspector, and this revealed the mystery of why trying to provide your own quotes results in double quotes. I've updated the question with the new findings, which invalidated some of my previously incorrect assumptions about where the double quoting was coming from. – AaronLS Jan 29 '15 at 16:04
14

I guess a little more convenient and structured way is to use Html helper. In your view it can be look like:

@{
 var htmlAttr = new Dictionary<string, object>();
 htmlAttr.Add("id", strElementId);
 if (!CSSClass.IsEmpty())
 {
   htmlAttr.Add("class", strCSSClass);
 }
}

@* ... *@

@Html.TextBox("somename", "", htmlAttr)

If this way will be useful for you i recommend to define dictionary htmlAttr in your model so your view doesn't need any @{ } logic blocks (be more clear).

Yaschur
  • 349
  • 2
  • 9
  • 4
    -1, never recommend someone to put logic in the views. Thew views should only be responsible for rendering. Add a HtmlHelper example instead and I'll give you +1. – jgauffin Nov 09 '11 at 08:13
  • 1
    To test things i can use code block in view - it's more quicker. And my recommendation was to move this block into model. – Yaschur Nov 09 '11 at 08:24
  • 3
    yes, but answers should show the best practice and not the quick and dirty version which makes code maintenance a nightmare. – jgauffin Nov 09 '11 at 08:28
  • @jgauffin I think Html helper here is unnecessary. see my answer. – gdoron Nov 09 '11 at 09:49
  • +1 @Yaschur Your answer is inspiring. Keep up the good work. ie. with C# explicit conversion feature, we can enhance this answer even more. Not all the code had to be shaped in a certain way. There is always a better way we are not aware of. And some project are in favor of code organization. Code organization are by practice not concepts! – Bamboo Dec 28 '12 at 10:41