209

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.

1
  • All of your first solutions would've worked if you wrapped them in a new instance of MvcHtmlString or used Html.Raw Commented Jul 23, 2013 at 19:43

5 Answers 5

397

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"))">
Sign up to request clarification or add additional context in comments.

5 Comments

I've accepted this as the answer, since it offers more options than mine.
The 2nd option causes the error The "div" element was not closed
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 ;)
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?)
How is your problem related to the posted question?
95

This:

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

will render this:

    <div class="details hide">

and is the mark-up I want.

3 Comments

I don't like having logic in views, even if it's trivial logic, I prefer using a ModelView object with a getDetailClass() method.
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. <div class="details @(@Model.HasDetails ? "show" : "hide")">
For those coming later as it adds confusion: @BigMike was talking of a view model (not a business/data model) which is shaped to match the need of the view as its name implies, so by construction it is aware of the view, and is the right place to put this kind of view related logic, especially if it can be shared between many views.
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"/>

4 Comments

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
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.
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.
The view is the right place. Format it nicely as variable assignments in a code block and it won't be messy.
8

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

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

Comments

1

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.

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.