Modify validator defaults:
// Override jquery validate plugin defaults in order to utilize Twitter Bootstrap 3 has-error, has-success, etc. styling.
$.validator.setDefaults({
highlight: function(element) {
$(element).closest('.form-group').addClass('has-error').removeClass('has-success');
},
unhighlight: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
},
errorElement: 'span',
errorClass: 'help-block',
errorPlacement: function (error, element) {
if (element.parent('.input-group').length || element.prop('type') === 'checkbox' || element.prop('type') === 'radio') {
error.insertAfter(element.parent());
} else {
error.insertAfter(element);
}
}
});
Create a new validation summary partial (so you have complete control over markup):
@model ModelStateDictionary
<div class="@(Html.ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors") panel panel-danger" data-valmsg-summary="true">
<div class="panel-heading">
Please, correct the following errors:
</div>
<div class="panel-body">
<ul>
@foreach (var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
{
<li>@modelError.ErrorMessage</li>
}
</ul>
</div>
</div>
Add one style to your CSS (to hide the validation summary on initial load without needing to depend on client-side scripting to hide it after the fact):
.validation-summary-valid {
display: none;
}
And render your alternative validation summary in your view instead of @Html.ValidationSummary():
@{Html.RenderPartial("_HtmlValidationSummary", ViewData.ModelState);}
And you're good to go. Oh, and to get the nice styling for checkbox and radio, depending on your setup the above script will catch it, but the easiest is to simply modify your i.e. Boolean.cshtml partial to be like the following (where Html.FormGroupFor is just a slightly updated version of https://github.com/erichexter/twitter.bootstrap.mvc/blob/master/src/Bootstrap/BootstrapSupport/ControlGroupExtensions.cs - just change "control-group" references to "form-group" and add " controlGroupWrapper.AddCssClass("has-error");" on line 58'ish):
@using Bctf.Web.HtmlHelpers
@model bool?
@using (Html.FormGroupFor(x => Model))
{
<label class="checkbox">
@Html.CheckBox("", Model.HasValue && Model.Value, new { @class = "tickbox-single-line" }) @ViewData.ModelMetadata.DisplayName
</label>
@Html.ValidationMessageFor(x => Model, string.Empty, new { @class = "help-block" })
}
All of the above is compilation of various solutions I've found from many different locations that have made the right mix. Ben's solution above works, but it does a fair bit client-side which causes a jitter whenever some method in it is called (i.e. post, page load, validation error). The above does all that you need without having all that DOM looping.
The various answers found at Bootstrap 3 with jQuery Validation Plugin cover much of the above (and credit for a significant chunk of the above goes to the various posters there, although no one answer covers it all).