Bootstrap validation in ASP.Net MVC
I use Bootstrap in almost every project I build these days. I build up a custom Bootstrap configuration to reduce bloat, and have my workflow down-pat for updating those configurations (which I’ll write about in the near future). I have familiarised myself with it’s ins-and-outs to help ensure I use it to it’s full potential without being overkill, and it has sped up my HTML build time immensely.
One issue I’ve come across before is finding harmony between ASP.Net MVC’s validation and Bootstrap’s validation classes.
Out of the box when you use Html.ValidationMessageFor() to output a validation (with none of Microsoft’s validation classes set up), this is what I end up with:
Behind the scenes, the Microsoft validation has put an input-validation-error
class on the text boxes that failed validation, and the validation message has some classes that can be styled:</span>
<span class="field-validation-error" data-valmsg-for="FullName" data-valmsg-replace="true">
<span id="FullName-error" class="">The Name field is required.</span>
</span>
Bootstrap’s method of validation styling is based on the parent’s class (the “form group”), instead of the element itself, thus allowing all children (e.g. labels and text boxes) to have a specific style:
<div class="form-group has-error">
<label class="control-label" for="inputError1">Input with error</label>
<input type="text" class="form-control" id="inputError1">
</div>
It’s not difficult to apply the Bootstrap validation classes when model errors exist using a little JavaScript, but I decided to look for a nice NuGet package to do this for me, and I found one - jquery.validate.unobtrusive.bootstrap by Sandro Caseiro. Simply install it via the NuGet Package Manager Console:
PM> Install-Package jquery.validate.unobtrusive.bootstrap
I use the bundling in the Microsoft.AspNet.Web.Optimization package, so rather than including the JavaScript library file raw in my view, I already have a validate bundle that will include it since this library has followed the naming convention, being jquery.validate.unobtrusive.bootstrap.js
, so I don’t even need to update the BundleConfig.cs file:
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include("~/Scripts/jquery.validate*"));
This bundle was already being included in my page using the following:
@Scripts.Render("~/bundles/jqueryval")
Unfortunately this didn’t work! The jquery.validate.unobtrusive.bootstrap.js was being included, but when I look at the source, it’s being included BEFORE the jquery.validate.unobtrusive.js file:
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.bootstrap.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>
The problem is that out of the box, the bundling library orders the packages included in the bundle alphabetically when using a wildcard *.
I saw a nice solution to this on someone’s the jquery.validate.unobtrusive.bootstrap GitHub issues page, it involves creating a custom orderer for the bundle:
Step 1: Create a new orderer class (I placed it within the BundleConfig class to keep it local):
private class JQueryValidateBundleOrderer : IBundleOrderer
{
public IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
{
// Sort files by number of dots in file name
return files.OrderBy(file => file.VirtualFile.Name.Count(ch => ch == '.'));
}
}
Step 2: Update the bundle creation code to use this orderer:
Bundle jQueryValidateBundle = new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*");
jQueryValidateBundle.Orderer = new JQueryValidateBundleOrderer();
bundles.Add(jQueryValidateBundle);
The result is that your scripts will be output in order of the “dots” - a kind of heirarchy assumed based on the file naming convention, which works in this particular situation, at least (and it would be great if it worked in general, signifying a naming convention for JavaScript files):
<script src="/Scripts/jquery.validate.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.bootstrap.js"></script>
And the result is a lovely looking form, utilising Bootstrap validation styling:
Job done!