Responsive Inline Elements with Twitter Bootstrap

Add Comment | Nov 12, 2013

imageTwitter Boostrap is a responsive css platform created by some dudes affiliated with Twitter and since supported and maintained by an open source following. I absolutely love the new version of this css toolkit. They rebuilt it with a mobile first strategy and it’s very easy to layout pages once you get the hang of it. Using a css / javascript framework like bootstrap is certainly much easier than coding your layout by hand. And, you get a “leg up” when it comes to adding responsive features to your site. Bootstrap includes column layout classes that let you specify size and placement based upon the viewport width. In addition, there are a handful of responsive helpers to hide and show content based upon the user’s device size. Most notably, the visible-xs, visible-sm, visible-md, and visible-lg classes let you show content for devices corresponding to those sizes (they are listed in the bootstrap docs.) hidden-xs, hidden-sm, hidden-md, and hidden-lg let you hide content for devices with those respective sizes.

These helpers work great for showing and hiding block elements. Unfortunately, there isn’t a provision yet in Twitter Bootstrap (as of the time of this writing) for inline elements. We are using the navbar classes to create a navigation bar at the top of our website, www.crowdit.com. When you shrink the width of the screen to tablet or phone size, the tools in the navbar are turned into a drop down menu, and a button appears on the right side of the navbar. This is great! But, we wanted different content to display based upon whether the items were on the navbar versus when they were in the dropdown menu. The visible-?? and hidden-?? classes make this easy for images and block elements. In our case, we wanted our anchors to show different text depending upon whether they’re in the navbar, or in the dropdown. span is inherently inline and it can be a block element.

My first approach was to create two anchors for each option, one set visible when the navbar is on a desktop or laptop with a wide display and another set visible when the elements converted to a dropdown menu. That works fine with the visible-?? and hidden-?? classes, but it just doesn’t seem that clean to me. I put up with that for about a week…last night I created the following classes to augment the block-based classes provided by bootstrap.

.cdt-hidden-xs, .cdt-hidden-sm, .cdt-hidden-md, .cdt-hidden-lg {
    display: inline !important;
}

@media (max-width:767px)
{
    .cdt-hidden-xs, .cdt-hidden-sm.cdt-hidden-xs, .cdt-hidden-md.cdt-hidden-xs, .cdt-hidden-lg.cdt-hidden-xs {
        display: none !important;
    }
}

@media (min-width:768px) and (max-width:991px) {
    .cdt-hidden-xs.cdt-hidden-sm, .cdt-hidden-sm, .cdt-hidden-md.cdt-hidden-sm, .cdt-hidden-lg.cdt-hidden-sm {
        display: none !important;
    }
}

@media (min-width:992px) and (max-width:1199px) {
    .cdt-hidden-xs.cdt-hidden-md, .cdt-hidden-sm.cdt-hidden-md, .cdt-hidden-md, .cdt-hidden-lg.cdt-hidden-md {
        display: none !important;
    }
}

@media (min-width:1200px) {
    .cdt-hidden-xs.cdt-hidden-lg, .cdt-hidden-sm.cdt-hidden-lg, .cdt-hidden-md.cdt-hidden-lg, .cdt-hidden-lg {
        display: none !important;
    }
}

.cdt-visible-xs, .cdt-visible-sm, .cdt-visible-md, .cdt-visible-lg {
    display: none !important;
}

@media (max-width:767px)
{
    .cdt-visible-xs, .cdt-visible-sm.cdt-visible-xs, .cdt-visible-md.cdt-visible-xs, .cdt-visible-lg.cdt-visible-xs {
        display: inline !important;
    }
}

@media (min-width:768px) and (max-width:991px) {
    .cdt-visible-xs.cdt-visible-sm, .cdt-visible-sm, .cdt-visible-md.cdt-visible-sm, .cdt-visible-lg.cdt-visible-sm {
        display: inline !important;
    }
}

@media (min-width:992px) and (max-width:1199px) {
    .cdt-visible-xs.cdt-visible-md, .cdt-visible-sm.cdt-visible-md, .cdt-visible-md, .cdt-visible-lg.cdt-visible-md {
        display: inline !important;
    }
}

@media (min-width:1200px) {
    .cdt-visible-xs.cdt-visible-lg, .cdt-visible-sm.cdt-visible-lg, .cdt-visible-md.cdt-visible-lg, .cdt-visible-lg {
        display: inline !important;
    }
}

I created these by looking at the example provided by bootstrap and consolidating the styles. “cdt” is just a prefix that I’m using to distinguish these classes from the block-based classes in bootstrap. You are welcome to change the prefix to whatever feels right for you. These classes can be applied to spans in textual content to hide and show text based upon the browser width. Applying the styles is simple…

<span class=”cdt-visible-xs”>This text is visible in extra small</span>

<span class=”cdt-visible-sm”>This text is visible in small</span>

Why would you want to do this? Here are a couple of examples, shown in screen shots.

image

This is the CrowdIt navbar on larger displays. Notice how the text is two line and certain words are capitalized? Now, check this out! Here is a screen shot showing the dropdown menu that’s displayed when the browser window is tablet or phone sized. The markup to make this happen is quite simple…take a look.

image

<li>
    <a href="@Url.Action("what-is-crowdit","home")" title="Learn about what CrowdIt can do for your Small Business">
        <span class="cdt-hidden-xs">WHAT<br /><small>is CrowdIt?</small></span>
        <span class="cdt-visible-xs">What is CrowdIt?</span>
    </a>
</li>

There is a single anchor tag in this example and only the spans change visibility based on browser width. I left them separate for readability and because I wanted to use the small tag; however, you could just as easily hide the “WHAT” and the br tag on small displays and replace them with “What “, consolidating this even further to text containing a single span.

<span class=”cdt-hidden-xs”>WHAT<br /></span><span class=”cdt-visible-xs”>What </span>is CrowdIt?

You might be a master of css and have a better method of handling this problem. If so, I’d love to hear about your solution…leave me some feedback! You’ll be entered into a drawing for a chance to win an autographed picture of ME! Yay!

Html.ValidationSummary and Multiple Forms

2 Comments | Nov 11, 2013

The Html.ValidationSummary helper writes a div with a list of general errors added to the model state while a request is being serviced. There is generally one form per view or partial view, I think, so often there is only one call to Html.ValidationSummary in the page resulting from the assembly of your views. And, consequently, there is no problem with the markup that Html.ValidationSummary spits out as a result. What if you want to put multiple forms in one view? Even if you create a view model that’s an aggregate of the view models for each form, the error validation summary is going to contain errors from both forms. Check out this screen shot, which shows a page with multiple forms. Notice how the error validation summary shows up twice. Grrr! Errors for the login form also show up in the registration form.

image

Luckily, there is an easy way around this. Pull the errors out of the model state and separate them for each form. You’ll need to identify the appropriate form by setting the key when you make calls to ModelState.AddModelError. Assume in my example that errors for the login form are added to model state using the “LoginForm” key. And, likewise, assume that errors for the registration form are added to model state using the “RegistrationForm” key. An example of that might look like this…

// If we got this far, something failed, redisplay form
ModelState.AddModelError("LoginForm", "User name or password is not right...");
return View(model);

Over in the code for your View, you can pull each form’s errors from the model state using lambda expressions that look like these…

var LoginFormErrors = ViewData.ModelState.Where(ms => ms.Key == "LoginForm");
var RegistrationFormErrors = ViewData.ModelState.Where(ms => ms.Key == "RegistrationForm");

Now that you have two collections containing errors, you can display only the errors specific to each form. I’m doing that in my code by removing the calls to Html.ValidationSummary and replacing them with enumerators that look like this…

if(LoginFormErrors.Count() > 0)
{
<div class="cdt-error-list">
    <ul>
    @foreach (var entry in LoginFormErrors)
    {
        foreach (var error in entry.Value.Errors)
        {
            <li>@error.ErrorMessage</li>
        }
    }
    </ul>
</div>
}

…and for the registration form, the code looks like this…

@if(RegistrationFormErrors.Count() > 0)
{
<div class="cdt-error-list">
    <ul>
    @foreach (var entry in RegistrationFormErrors)
    {
        foreach (var error in entry.Value.Errors)
        {
            <li>@error.ErrorMessage</li>
        }
    }
    </ul>
</div>
}

The result is a nice clean separation of the list of errors that are specific to each form. And, this is important because each form is submitted separately in my case, so both forms don’t generate errors in the same context. As you’ll see in the screen shot below, errors added to the model state when the login form is submitted do not show up in the registration form’s validation summary.

image

JavaScript JSON Error While Tabbing in ASP.NET MVC

Add Comment | Nov 11, 2013

I sometimes don’t care about validation for a specific control. The RememberMe control in the login form, for example, really doesn’t need validation, so I forget to include the Html.ValidationMessageFor helper line for that control in particular. As a result, when I’m debugging using IE, I get a silly JSON parsing exception when changing focus from one field to another. The exception doesn’t hurt anything, as far as I know, but it’s just plain annoying. If you’re getting this error, and you don’t want validation messages showing up for controls on a form, you can put them in div tags and set the display style on the divs to none. When I have a handful of controls that I don’t want the validation messages for, I just throw them all in the same div and hide it.