How to use Twitter Bootstrap popovers for jQuery validation notifications?

JqueryValidationTwitter BootstrapPopover

Jquery Problem Overview


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?

Jquery Solutions


Solution 1 - Jquery

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); 
    }
});

enter image description here

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:

enter image description here

Check out the jsFiddle.

Solution 2 - Jquery

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.

Solution 3 - Jquery

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?

Solution 4 - Jquery

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'
});

Solution 5 - Jquery

This jQuery extension for jQuery Validation Plugin (tested with version 1.9.0) will do the trick.

https://github.com/tonycoco/rails_template/blob/master/files/assets/javascripts/jquery.validate.bootstrap.js

This also adds in some Rails-esk error messaging.

Solution 6 - Jquery

I prefer to change the CSS of bootstrap. Just added the classes of jQuery validate in the right place. field-validation-error and input-validation-error

    form .clearfix.error > label, form .clearfix.error .help-block, form .clearfix.error .help-inline, .field-validation-error {
  color: #b94a48;
}
form .clearfix.error input, form .clearfix.error textarea, .input-validation-error {
  color: #b94a48;
  border-color: #ee5f5b;
}
form .clearfix.error input:focus, form .clearfix.error textarea:focus, .input-validation-error:focus {
  border-color: #e9322d;
  -webkit-box-shadow: 0 0 6px #f8b9b7;
  -moz-box-shadow: 0 0 6px #f8b9b7;
  box-shadow: 0 0 6px #f8b9b7;
}

Solution 7 - Jquery

This is how I did it with Bootstrap 2.x and jQuery Validate 1.9

$('#form-register').validate({ errorElement: 'span', errorClass:'help-inline', highlight:    function (element, errorClass) {
        $(element).parent().parent().addClass('error');
    }, unhighlight: function (element, errorClass) {
        $(element).parent().parent().removeClass('error');
    }});

Solution 8 - Jquery

Please take a look at the following:

EDIT

Code from link:

$('form').validate({
    rules: {
        numero: {
            required: true
        },
        descricao: {
            minlength: 3,
            email: true,
            required: true
        }
    },

    showErrors: function (errorMap, errorList) {

        $.each(this.successList, function (index, value) {
            $(value).popover('hide');
        });


        $.each(errorList, function (index, value) {

            console.log(value.message);

            var _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>'
            });

            _popover.data('popover').options.content = value.message;

            $(value.element).popover('show');

        });

    }

});

Solution 9 - Jquery

Many thanks for the heads up! Here is my version for Bootstrap but with Tooltips. In my opinion it's more elegant than popovers. I know the question was for popovers so please do not vote down for this reason. Maybe somebody will like it this way. I love when I'm searching for something and I found new ideas on Stackoverflow. Note: no markup on form is necessary.

	$('#LoginForm').validate({
		rules: {
			password: {
				required: true,
				minlength: 6
			},

			email_address: {
				required: true,
				email: true
			}
		},
		messages: {
			password: {
				required: "Password is required",
				minlength: "Minimum length is 6 characters"
			},
			email_address: {
				required: "Email address is required",
				email: "Email address is not valid"
			}
		},	
		submitHandler: function(form) {
			form.submit();
		},

		showErrors: function (errorMap, errorList) {

			$.each(this.successList, function (index, value) {
				$('#'+value.id+'').tooltip('destroy');
			});


			$.each(errorList, function (index, value) {

				$('#'+value.element.id+'').attr('title',value.message).tooltip({
					placement: 'bottom',
					trigger: 'manual',
					delay: { show: 500, hide: 5000 }
				}).tooltip('show');

			});

		}

	}); 

Solution 10 - Jquery

This is how I made it happen. But it involves making 2 changes to the validate script (I got the code for bootstrap 1.4 here and then modified it - http://mihirchitnis.net/2012/01/customizing-error-messages-using-jquery-validate-plugin-for-twitter-bootstrap/)

My call to validate:

    $("#loginForm").validate({
  errorClass: "control-group error",
  validClass: "control-group success",
  errorElement: "span", // class='help-inline'
  highlight: function(element, errorClass, validClass) {
	if (element.type === 'radio') {
		this.findByName(element.name).parent("div").parent("div").removeClass(validClass).addClass(errorClass);
	} else {
		$(element).parent("div").parent("div").removeClass(validClass).addClass(errorClass);
	}
  },
  unhighlight: function(element, errorClass, validClass) {
	if (element.type === 'radio') {
		this.findByName(element.name).parent("div").parent("div").removeClass(errorClass).addClass(validClass);
	} else {
		$(element).parent("div").parent("div").removeClass(errorClass).addClass(validClass);
	}
  }
});

Then you need to change 2 things in jquery.validate.js

  1. apply this fix - https://github.com/bsrykt/jquery-validation/commit/6c3f53ee00d8862bd4ee89bb627de5a53a7ed20a
  2. After line 647 (in the showLabel function, create label part) after line .addClass(this.settings.errorClass) add line: .addClass("help-inline")
    Someone can maybe find a way to apply the second fix in the validate function, but I havent found a way, since showLabel is called after highlight.

Solution 11 - Jquery

This is what I put in my validate to conform to the Twitter Bootstrap guidelines. The error validation message is put in a <span class=help-inline> and we want to highlight the outer container as an error or success:

errorClass:'help-inline',
errorElement:'span',
highlight: function (element, errorClass, validClass) {
$(element).parents("div.clearfix").addClass('error').removeClass('success');
},
unhighlight: function (element, errorClass, validClass) {
$(element).parents(".error").removeClass('error').addClass('success');
}

Solution 12 - Jquery

Here is an update to Kenny Meyer's excellent answer above. There were a couple of issues that prevented it from working for me, which I have addressed in this snippet:

showErrors: function (errorMap, errorList) {
        $.each(this.successList, function (index, element) {
            return $(element).popover("destroy");
        });
        
        $.each(errorList, function (index, error) {
            var ele = $(error.element); //Instead of referencing the popover directly, I use the element that is the target for the popover

            ele.popover({
                    trigger: "manual",
                    placement: "top",
                    content: function(){ //use a function to assign the error message to content
                        return error.message
                    },
                    template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><div class="popover-content"><p></p></div></div></div>'
            });
            
            //bs.popover must be used, not just popover
            ele.data("bs.popover").options.content = error.message;
            
            return $(error.element).popover("show");
        });
    }

Solution 13 - Jquery

Not sure if this is relevant to the discussion because the original poster asked for hooks to show/hide bootstrap popovers.

I was looking for simple validation and popovers didn't matter. A related post and the first in google search results has already been marked duplicate of this question. So it made sense to mention this excellent @ReactiveRaven's jqValidation JS, aptly called jqBootstrapValidation, that weds well with Twitter Bootstrap. Setup takes a few minutes only. Download here.

Hope this adds value.

Solution 14 - Jquery

tl;dr avoid needing to enumerate explicit popovers by using a hash map to store the ids of the elements, and creating popovers on-the-fly (mashup Jeffrey Gilbert and Kenny Meyer's approaches).

Here's my take, which fixes the flickering problem mentioned by others, but unlike @Jeffrey Gilbert's answer, does not use a list (errorStates) but rather uses an error map. Hash maps FTW. I think I remember reading somewhere that every problem in CS can be solved with a hash map :)

var err_map = new Object();     // <--- n.b.
$("form#set_draws").validate({
  rules: {
    myinput: { required: true, number: true },
  },
  showErrors: function(errorMap, errorList) {
    $.each(this.successList, function(index, value) {
      if (value.id in err_map)
      {
        var k = err_map[value.id];
        delete err_map[value.id]; // so validation can transition between valid/invalid states
        k.popover("hide");
      }
    });
    return $.each(errorList, function(index, value) {
      var element = $(value.element);
      if( ! (value.element.id in err_map) ) {
        var _popover = 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>"
        });
        _popover.data("popover").options.content = value.message;
          err_map[value.element.id] = _popover;
        return err_map[value.element.id].popover("show");
      }
    });
  }
});

Thanks to all others who posted ideas on this.

Solution 15 - Jquery

Checkout this: https://github.com/mingliangfeng/jquery.validate.bootstrap.popover

It shows how to use Bootstrap popover css, instead of JS. JS method popup will cause blinking issue.

Solution 16 - Jquery

If using the above Kenny Meyer code for popups, beware that rules that check a field's content but isn't required such as a valid URL will cause the popup to not disappear upon clearing the field. See below onkeyup for solution. If anyone has a better solution, please post.

onkeyup: function(element, event) {
			if($(element).valid())  {
				return $(element).popover("hide");
			}
		}
		

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSynchroView Question on Stackoverflow
Solution 1 - JqueryKenny MeyerView Answer on Stackoverflow
Solution 2 - JqueryChris FulstowView Answer on Stackoverflow
Solution 3 - JqueryVarun SinghView Answer on Stackoverflow
Solution 4 - JqueryJeffrey GilbertView Answer on Stackoverflow
Solution 5 - JquerytonycocoView Answer on Stackoverflow
Solution 6 - JqueryAlberto ChvaicerView Answer on Stackoverflow
Solution 7 - JqueryJamie R RytlewskiView Answer on Stackoverflow
Solution 8 - JqueryCodematorView Answer on Stackoverflow
Solution 9 - JqueryAdrian P.View Answer on Stackoverflow
Solution 10 - JqueryAndrej BergantView Answer on Stackoverflow
Solution 11 - JquerynaminView Answer on Stackoverflow
Solution 12 - JquerytofiriusView Answer on Stackoverflow
Solution 13 - JqueryuchampView Answer on Stackoverflow
Solution 14 - Jqueryuser67416View Answer on Stackoverflow
Solution 15 - JqueryAndrew FengView Answer on Stackoverflow
Solution 16 - JquerymonsoonView Answer on Stackoverflow