Cleaner Conditional HTML Attributes In Razor Web Pages

Every so often, you will want to conditionally render HTML attributes or their values within your Razor Web Pages based on the outcome of some runtime logic. Often, the logic required to manage this can become messy and lead to unnecessary spaghetti code. This article explores a few common scenarios and provides some solutions, as well as introducing a nice new feature that was released as part of Razor v 2.0.

The first scenario involves dynamically applying one value or another to an attribute based on a condition. A typical example would be when you want to apply an alternating style to items in a list. Various ASP.NET Grid controls offer this functionality built-in with AlternatingItem templates or in the case of the WebGrid, an alternatingRowStyle parameter that takes a CSS class name as a value. But if you simply want to display a list of items without the aid of a control, you have to manage this yourself.

Most solutions that you see make use of a counter of some kind when attempting to calculate which style to apply. As each item is rendered, the counter is incremented by one and then tested to see if it is odd or even using an if... else construct. The basis of this approach is sound, although there is a slightly nicer way to establish the relative position of an item in the list rather than having to maintain a counter, and that is to use its index. Here is some sample code that obtains a list of employees from the Northwind database and then renders them to the browser:

@{
    var db = Database.Open("Northwind");
    var data = db.Query("SELECT TitleOfCourtesy, FirstName, LastName FROM Employees");
    var people = data.Select((person, index) => new {Index = index, Person = person});
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
        <style>
            #content { width: 400px; font: 0.8em Arial; }
            .even { background-color:  #dcf1b8; }
            .odd { background-color: #f4ffe1; }
        </style>
    </head>
    <body>
        <div id="content">
        @foreach(var record in people){
            <div class="@(record.Index % 2 == 0 ? "even" : "odd")">
                @record.Person.TitleOfCourtesy @record.Person.FirstName @record.Person.LastName
            </div>
        }
        </div>
    </body>
</html>

The first two lines of code are common enough. Data is retrieved from the database using a simple SELECT statement. The last line in the code block uses an overload of the Enumerable.Select extension method that projects each element in the sequence (the data from the database) into a new form while incorporating the element's index. The "new form" is a sequence of anonymous types that each have an Index property which is assigned the value if the item's (zero-based) index in the original sequence, and a Person property which is assigned the value of the item itself (a DynamicRecord object).

You can see a couple of CSS styles declared in the head of the document, and then the people variable is iterated over. The C# conditional operator (also known as the Ternary operator) is used as a shorthand for if... else. It tests to see if the current item's index value, when divided by 2 leaves a remainder or not. If there is no remainder, the value "even" is applied to the div's class attribute. Otherwise the value "odd" is applied, resulting in alternating CSS styles:

The next examples take advantage of a feature which was added to Razor v 2.0. The feature is called Conditional Attributes, and it allows you to decide whether to render the attribute at all, let alone a value for it. The first example shows how this feature is can be used to determine whether to render an attribute or not. The scenario features a navigation, where the link to the current page has a CSS class applied to it to make it look different to the other links in the navigation. Here is the code for the Layout page:

@{
    
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
        <style>
            body { font-family: Arial; font-size: 0.8em; }
            nav ul { list-style-type: none; margin: 0; padding: 0; }
            nav li { float: left; }
            nav li a { display: block; padding-right: 15px; color: #0026ff; text-decoration:  underline; }
            nav li a.current { text-decoration:  none; color: black; }
            .clear { clear: both; }
            #content{ width:  300px; font-family: "Times New Roman"; }
            .cap { font-size: 4.2em; float: left; color: #a01820; padding-right: 7px; }
        </style>
    </head>
    <body>
        <nav>
            <ul>
                <li><a href="~/Page1" class="@IsCurrentPage("Page1")">Page 1</a></li>
                <li><a href="~/Page2" class="@IsCurrentPage("Page2")">Page 2</a></li>
                <li><a href="~/Page3" class="@IsCurrentPage("Page3")">Page 3</a></li>
            </ul>
        </nav>
        <div class="clear"></div>
        @RenderBody()
    </body>
</html>
@functions {
    public static string IsCurrentPage(string page){
        return HttpContext.Current.Request.Url.ToString().Contains(page) ? "current" : null;
    }
}

A default style has been applied to all hyperlinks in the nav element, which ensures that they appear in blue and underlined. Another style has been declared for a link with the class attribute of "current" which results in the link appearing in black font with no underline. There's a little bit of CSS floaty stuff there too, so that the list of links appear horizontally instead of vertically. The value for the class attribute of each link is derived from a function called IsCurrentPage. The function takes a string representing the name of the page and returns "current" if that page name is found within the current URL. If it is not found, the function returns null, which is important as you will see.

Here is an image showing Page1.cshtml:

And here is the source for the <nav> element:

Notice that "current" has been applied to the class attribute for the first link. The class attribute is not rendered at all for the other two links where the value returned from the method was null. If the value had been (almost) anything else, including an empty string, the class attributes would have been rendered with that value. In the case of an empty string, it would have appeared as class="". Did you spot the "almost" there? There is another pair of values that Razor's Conditional Attributes feature responds to in a special way, and that is true and false.

This example features a dropdown list, which is used to display the selected product's category:

@{
    var db = Database.Open("Northwind");
    var id = UrlData[0].IsEmpty() ? 1 : UrlData[0].AsInt();
    var product = db.QuerySingle("SELECT ProductName, CategoryId from Products WHERE ProductId = @0", id);
    var categories = db.Query("SELECT CategoryId, CategoryName FROM Categories");
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div>
            @product.ProductName
            <select name="CategoryId">
                @foreach(var category in categories){
                    <option value="@category.CategoryId" selected="@Selected(product.CategoryId, category.CategoryId)">
                        @category.CategoryName
                     </option>
                }
            </select>
        </div>
    </body>
</html>
@functions {
    public static bool Selected(int a, int b){
        return a == b;
    }
}
In this case, the value of the selected attribute in each option element has been set to the output of a function which returns a bool. The function simply compares the current category's ID with that of the selected product. If they match, the function returns true. Otherwise it returns false. If the value applied to the attribute is true, the result is that the attribute is repeated, in this case resulting in selected="selected". And that is perfect for options in a dropdown list. If the value equates to false, nothing is rendered just as in the previous example when null was applied to the value of an attribute. Here is the source for the rendered dropdown list when a product in the Beverages category is displayed.

 

There are other attributes, which when the attribute name is repeated have special meaning. Two of them are checked, and disabled. Here is an example where both are applied through the same variable:

@{
    var disabled = Request["toggle"] == "on";
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <form method="post">
            <input type="checkbox" name="toggle" checked="@disabled" />
            <input type="text" name="text" disabled="@disabled" />
            <input type="submit" />
        </form>
    </body>
</html>
This very simple example renders a checkbox and a textbox. If the checkbox is ticked, the default value for checkboxes "on" is passed in the Request.Form collection. The single line of code at the top of the page applies the value true to the disabled variable in that case. And here is the source of the form when that happens. As you can see, both attributes have been repeated as desired:

 

Finally, class attributes can have multiple values separated by spaces.

@{
    Layout = "~/_Layout.cshtml";
    string red = null;
    string black = "red";
}
<div id="content" class="@red @black">
    <span class="cap">A</span>liquam mi turpis, rutrum vitae sagittis ut, fermentum at 
    massa. Mauris a elit libero, non tincidunt nunc. Donec ac sem eros. Suspendisse 
    tincidunt sollicitudin adipiscing. Nullam cursus iaculis purus, vel iaculis urna 
    porttitor id. Aenean et lorem vel est tristique luctus. Morbi pharetra justo non 
    urna aliquet ut sollicitudin dolor consectetur. Curabitur tincidunt eleifend erat, 
    et pharetra nisi sagittis vel. Vivamus quis fermentum purus. Proin tellus diam, 
    accumsan sit amet feugiat vel, blandit sed turpis.
</div>

If the variable used to set any of them resolves to null, not only is it not rendered, but any remaining whitespace is collapsed:

Conditional attributes provide a nice addition to Razor allowing the Web Pages developer to manage scenarios that can result in messy spaghetti code in a much more succinct manner. In addition to this, you can use the conditional operator to keep if... else tests to one clean inline expression, reducing code smell.

All of the scenarios and sample code are available in the GitHub Repo that accompanies this article.

 

You might also like...