How to use Twitter Bootstrap popovers for jQuery validation notifications?
I can make popovers appear using bootstrap easily enough, and I can also do validations using the standard jQuery validation plugin or the jQuery validation engine, but I can't figure out how to feed one into the other.
I think what I need is some hook which is called by the validator when it wants to display a notification, give it a closure that passes the message and the target element to a popover. This seems like a kind of dependency injection.
All nice in theory, but I just can't figure out where that hook is, or even if one exists in either validation engine. They both seem intent on taking responsibility for displaying notifications with all kinds of elaborate options for placement, wrappers, styles when all I'm after is the error type(s) (I don't necessarily even need message text) and element it relates to. I've found hooks for the entire form, not the individual notifications.
I much prefer validation systems that use classes to define rules, as they play nicely with dynamically created forms.
Anyone have a solution or a better idea?
This is a hands-on example:
$('form').validate({
errorClass:'error',
validClass:'success',
errorElement:'span',
highlight: function (element, errorClass, validClass) {
$(element).parents("div[class='clearfix']").addClass(errorClass).removeClass(validClass);
},
unhighlight: function (element, errorClass, validClass) {
$(element).parents(".error").removeClass(errorClass).addClass(validClass);
}
});
It doesn't really use bootstrap popovers, but it looks really nice and is easy to achieve.
UPDATE
So, to have popover validation you can use this code:
$("form").validate({
rules : {
test : {
minlength: 3 ,
required: true
}
},
showErrors: function(errorMap, errorList) {
$.each(this.successList, function(index, value) {
return $(value).popover("hide");
});
return $.each(errorList, function(index, value) {
var _popover;
_popover = $(value.element).popover({
trigger: "manual",
placement: "top",
content: value.message,
template: "<div class=\"popover\"><div class=\"arrow\"></div><div class=\"popover-inner\"><div class=\"popover-content\"><p></p></div></div></div>"
});
// Bootstrap 3.x :
//_popover.data("bs.popover").options.content = value.message;
// Bootstrap 2.x :
_popover.data("popover").options.content = value.message;
return $(value.element).popover("show");
});
}
});
You get something like this:
Check out the jsFiddle.
Take a look at the highlight
and showErrors
jQuery Validator options, these will let you hook in your own custom error highlights that trigger Bootstrap popovers.
Chris Fulstow had it right, but it still took me a while, so heres the complete code:
This shows the popover on error, and hides the default error labels:
$('#login').validate({
highlight: function(element, errClass) {
$(element).popover('show');
},
unhighlight: function(element, errClass) {
$(element).popover('hide');
},
errorPlacement: function(err, element) {
err.hide();
}
}).form();
This sets up the popover. The only thing you need from this is trigger: 'manual'
$('#password').popover({
placement: 'below',
offset: 20,
trigger: 'manual'
});
The title and content attributes passed in to popover weren't working, so I specified them inline in my #password input with data-content='Minimum 5 characters' and data-original-title='Invalid Password'. You also need rel='popover' in your form.
This works, but the popover flickers upon unselecting. Any idea how to fix that?
Here's a follow up to the excellent suggestion from Varun Singh which prevents the "flicker" issue of the validation constantly trying to "show" even though the popup is already present. I've simply added an error states array to capture which elements are showing errors and which aren't. Works like a charm!
var errorStates = [];
$('#LoginForm').validate({
errorClass:'error',
validClass:'success',
errorElement:'span',
highlight: function (element, errorClass) {
if($.inArray(element, errorStates) == -1){
errorStates[errorStates.length] = element;
$(element).popover('show');
}
},
unhighlight: function (element, errorClass, validClass) {
if($.inArray(element, errorStates) != -1){
this.errorStates = $.grep(errorStates, function(value) {
return value != errorStates;
});
$(element).popover('hide');
}
},
errorPlacement: function(err, element) {
err.hide();
}
});
$('#Login_unique_identifier').popover({
placement: 'right',
offset: 20,
trigger: 'manual'
});
$('#Login_password').popover({
placement: 'right',
offset: 20,
trigger: 'manual'
});