204

While Microsoft has created some automagic rendering of html attributes in razor MVC4, it took me quite some time to find out how to render a second css class on an element, based on a conditional razor expression. I would like to share it with you.

Based on a model property @Model.Details, I want to show or hide a list item. If there are details, a div should be shown, otherwise, it should be hidden. Using jQuery, all I need to do is add a class show or hide, respectively. For other purposes, I also want to add another class, "details". So, my mark-up should be:

<div class="details show">[Details]</div> or <div class="details hide">[Details]</div>

Below, I show some failed attempts (resulting mark-up assuming there are no details).

This: <div @(@Model.Details.Count > 0 ? "class=details show" : "class=details hide")>,

will render this: <div class="details" hide="">.

This: <div @(@Model.Details.Count > 0 ? "class=\"details show\"" : "class=\"details hide\"")>.

will render this: <div class=""details" hide&quot;="">.

This: <div @(@Model.Details.Count > 0 ? "class='details show'" : "class='details hide'")>

will render this: <div class="'details" hide&#39;="">.

None of these are correct mark-up.

Community
  • 1
  • 1
R. Schreurs
  • 8,587
  • 5
  • 43
  • 62
  • All of your first solutions would've worked if you wrapped them in a new instance of MvcHtmlString or used Html.Raw – Kyle Jul 23 '13 at 19:43

5 Answers5

390

I believe that there can still be and valid logic on views. But for this kind of things I agree with @BigMike, it is better placed on the model. Having said that the problem can be solved in three ways:

Your answer (assuming this works, I haven't tried this):

<div class="details @(@Model.Details.Count > 0 ? "show" : "hide")">

Second option:

@if (Model.Details.Count > 0) {
    <div class="details show">
}
else {
    <div class="details hide">
}

Third option:

<div class="@("details " + (Model.Details.Count>0 ? "show" : "hide"))">
von v.
  • 16,868
  • 4
  • 60
  • 84
  • 2
    I've accepted this as the answer, since it offers more options than mine. – R. Schreurs Dec 04 '14 at 14:16
  • 20
    The 2nd option causes the error `The "div" element was not closed` – intrepidis Apr 15 '15 at 14:55
  • 12
    Of course it will as what's written here is not the complete code but rather the part of the code that is in question. Who knows how many other elements are in the div ;) – von v. Apr 16 '15 at 00:34
  • 1
    Didn't work for me. I got this error ``'ClubsModel' does not contain a definition for 'ClubsFilter' and no extension method 'ClubsFilter' accepting a first argument of type 'ClubsModel' could be found (are you missing a using directive or an assembly reference?)`` – Martin Erlic May 09 '18 at 05:27
  • 3
    How is your problem related to the posted question? – von v. May 11 '18 at 04:16
91

This:

    <div class="details @(Model.Details.Count > 0 ? "show" : "hide")">

will render this:

    <div class="details hide">

and is the mark-up I want.

Sanchitos
  • 8,423
  • 6
  • 52
  • 52
R. Schreurs
  • 8,587
  • 5
  • 43
  • 62
  • 1
    I don't like having logic in views, even if it's trivial logic, I prefer using a ModelView object with a getDetailClass() method. – BigMike Mar 29 '13 at 09:47
  • 36
    Personally I prefer the trivial logic, having a getDetailCssClass method means that your Model is aware of your View, breaking down that abstraction. I would add an HasDetails method to the Model to reduce the logic required in the view, then leave the css class logic to the view, that means you don't have to litter the view with `@Model.Details.Count > 0`. e.g. `
    `
    – Chris Diver Oct 01 '13 at 09:47
31

You can add property to your model as follows:

    public string DetailsClass { get { return Details.Count > 0 ? "show" : "hide" } }

and then your view will be simpler and will contain no logic at all:

    <div class="details @Model.DetailsClass"/>

This will work even with many classes and will not render class if it is null:

    <div class="@Model.Class1 @Model.Class2"/>

with 2 not null properties will render:

    <div class="class1 class2"/>

if class1 is null

    <div class=" class2"/>
syned
  • 2,201
  • 19
  • 22
  • 11
    I think it's better to let the view define things such as the css classes. Remember that the view should be able to be deply modified (or even replaced) without it affecting the View Model – tobiak777 Jun 08 '15 at 12:54
  • 1
    Although I agree with reddy in general, there might be cases in which it can be justified to do it the way syned says. I did it exactly like that. In my case I'm relying on a ViewModel object full of information for rendering the view, it's not just a data object. – Gonzalo Méndez Apr 11 '16 at 16:40
  • 1
    I'd use it like this if there were more than 2 outcomes. For example for 5 possible classes. Than it'd be messy to keep it in view. – cah1r Apr 19 '16 at 06:55
  • 1
    The view is the right place. Format it nicely as variable assignments in a code block and it won't be messy. – Tom Blodget Oct 10 '16 at 20:32
7

You can use String.Format function to add second class based on condition:

<div class="@String.Format("details {0}", Details.Count > 0 ? "show" : "hide")">
0

A more reusable approach inside your Razor code:

@functions {
    public static string Displayed(int count)
    {
        return count > 0 ? "show" : "hide";
    }
}

Using it:

<div class="@Displayed(Details.count)">Details content</div>

Which you can reuse anywhere in your code.

carloswm85
  • 1,396
  • 13
  • 23