How to use Twitter Bootstrap popovers for jQuery validation notifications?

余生长醉 提交于 2019-11-27 05:50:44

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.

Kenny Meyer

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.

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

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.

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

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

Please take a look at the following:
- https://gist.github.com/3030983
I think it's the simplest of all.

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

        });

    }

});

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

            });

        }

    }); 

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.

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

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

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.

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.

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.

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");
            }
        }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!