1

I've implemented the excellent method to use Bootstrap 3's Popovers to show validation error messages, and for the most part it works well - except when it comes to hidden or replaced elements, like Select2 or CKEditor. The popover positions itself correctly on regular elements like input and normal selects, but not a select enhanced by Select 2.

I've created a Fiddle that Sparky fixed so it actually works: http://jsfiddle.net/jemxtthb/4/

$("#noticeSentTo").select2();

var validator = $("#documentAdmin").validate({
        debug: true,
        ignore: "",
        showErrors: function(errorMap, errorList) {
            $.each(this.successList, function(index, value) {
            return $(value).popover("hide").parents(".form-group").removeClass('has-error').addClass('has-success');
            });
            return $.each(errorList, function(index, value) {
            var _popover;
            _popover = $(value.element).popover({
                trigger: "manual",
                placement: "auto top",
                content: value.message,
                container: "body",
                template: "<div class=\"popover\" role=\"tooltip\"><div class=\"arrow\"></div><div class=\"popover-content\"><p></p></div></div>"
            });  
            _popover.data("bs.popover").options.content = value.message;
            return $(value.element).popover("show").parents(".form-group").addClass('has-error').removeClass('has-success');
        });
        }
    });
});

Here's a screen capture of what I see within my application: Popover not positioned correctly

I've tried using the selector option on the popovers, but can't figure out how to specify the current element the popover's being applied to (if that's even the right way to do it).

As you can see from my screen capture, the popover appears up and to the far left of the element (in red) it's supposed to be bound to because Select2 is hiding the original select.

UPDATE: Based on @Sparky's suggestion, I've changed it to use errorPlacement instead of the method I used previously - the only issue now is getting the condition where if it's a hidden element (like Select2 or CKEditor) to provide the Popup to the parent object instead of the element itself (since Popovers cannot work on hidden elements, apparently) - I've put in some debug console.log code just to make sure the if statement in the jQuery is firing correctly:

var validator = $("#documentAdmin").validate({
    ignore: [],
    errorPlacement: function (error, element) {

        var lastError = $(element).data('lastError'),
                newError = $(error).text();

        $(element).data('lastError', newError);

        if (newError !== '' && newError !== lastError) {
                $(element).popover({
                    trigger: "manual",
                    placement: "auto top",
                    content: newError,
                    container: "body",
                    template: "<div class=\"popover\" role=\"tooltip\"><div class=\"arrow\"></div><div class=\"popover-content\"><p></p></div></div>"
            });
             if (element.is(':hidden')) {
                    // $(element).next().parents(".form-group").addClass('has-error').removeClass('has-success').popover("show");
                    $(element).next('span').popover('show').addClass('has-error').removeClass('has-success');
                    console.log('hidden element');
             } else {
            $(element).popover("show").parents(".form-group").addClass('has-error').removeClass('has-success');
                console.log('normal element');
            }
        }
    },  
    success: function (label, element) {
        $(element).popover("hide").parents(".form-group").removeClass('has-error').addClass('has-success');
    }
});

And here's an updated Fiddle: http://jsfiddle.net/jemxtthb/6/

OK - really odd - the Fiddle above actually works as expected! Now I need to figure out why it works in the Fiddle and not my app...

Any help/guidance is appreciated!

Joyrex
  • 1,103
  • 14
  • 24
  • How about to set the `tooltip container` element near your input box but not to the body. – R Lam May 06 '15 at 02:44
  • 1
    instead of $(value.element).popover attach the popover to the form-group. $(value.element).closest('.form-group').popover. – bassxzero May 06 '15 at 02:57
  • Try setting the `dropdownParent` to the container where your ` – Kevin Brown-Silva May 06 '15 at 03:10
  • Why didn't you construct the jsFiddle to show the issue? – Sparky May 06 '15 at 13:31
  • For some reason the Fiddle wouldn't work, despite having everything that is working in my environment there. I'll try the suggestions offered so far and report back my findings. – Joyrex May 06 '15 at 14:26
  • I tried the above suggestions: setting the tooltip container from 'body' to 'div' (I also tried '
    " resulted in my page locking up - I suspect it was trying to apply it to EVERY form-group on my page. On Select2, I tried adidng the dropdownParent to "form-group", but still the Popover positioned itself off to the left. FYI this is Select2 with the Bootstrap 3 theme in use.
    – Joyrex May 06 '15 at 15:48
  • There's not much anybody can do for you when the jsFiddle does not show the problem and/or is broken (Check the console and view your errors). BTW, you cannot validate a `select` element if it's already pre-loaded with a selection. The first `option` needs to contain `value=""`, otherwise the validation for this element will never fire because there's nothing to validate. – Sparky May 07 '15 at 13:42

2 Answers2

1

Looking at the errors in the JavaScript console and fixing the following in your jsFiddle...

  1. There is no point in having a select element with validation if it comes pre-loaded with a value. In other words, there is nothing to validate if every option already contains a value or there is an option with a value that has the selected attribute. Typically, the first option contains value="".

    <option value="">Please select<option>
    
  2. There was a 404 error on your Bootstrap script file. The CDN URL was mangled.

  3. Not critical, but it should be ignore: [], not ignore: "".

  4. You're getting a "no 'name' assigned" error in the debug console because the hidden input element created by Select2 does not contain a name attribute. It's apparently not an issue now, but it could be an issue when you have more elements on the page. The jQuery Validate plugin mandates that all elements to be validated contain a name attribute.

DEMO: http://jsfiddle.net/jemxtthb/4/


for the most part it works well - except when it comes to hidden or replaced elements, like Select2 or CKEditor. The popover positions itself correctly on regular elements like input and normal selects, but not a select enhanced by Select 2.

You should not use showErrors for tooltips because this callback function is typically used for generating a list of all messages, like for a summary at the top of the form. "Gets the map of errors as the first argument and an array of errors as the second."

Instead, you'll need to integrate your tooltips plugin with jQuery Validate by using its errorPlacement and success callback functions; which will put the single pending error message inside a single tooltip. This integration also depends on the available options of your popovers plugin... like can you dynamically change the text inside a tooltip? Can you dynamically/programmatically show/hide them, etc?

Then within errorPlacement you can conditionally target certain elements to tweak the placement when the element is hidden by Select2, etc.

Something more like this...

$("#documentAdmin").validate({
    ignore: [],
    errorPlacement: function (error, element) {
        // put text of error into tooltip with $(error).text()
        // conditional placement of the tooltip
        // show the tooltip
    },
    success: function (label, element) {
        // hide the tooltip
    },
    // any other options
});

I'm using the ToolTipster jQuery plugin like this.

Community
  • 1
  • 1
Sparky
  • 98,165
  • 25
  • 199
  • 285
  • Thanks for the detailed answer and opinions - since I'm bound to Bootstrap for this project, I'm not sure if I can use the ToolTipster plugin (I'll have a look at their site and see about Bootstrap integration). I'll give your suggestions a go and report back - hopefully with a "answered" mark on your response! – Joyrex May 07 '15 at 17:16
  • @Joyrex, I am absolutely not recommending that you use the Tooltipster plugin. I only linked to it as an example of how you would need to use `errorPlacement` and `success` to integrate your Popover plugin. – Sparky May 07 '15 at 17:18
  • Of course - I'm looking into how to use `errorPlacement` to generate the Bootstrap Popover similar to how I had it set up (incorrectly) in `showErrors`. – Joyrex May 07 '15 at 17:52
0

As it turns out, the above code worked correctly from the beginning - after @Sparky fixed my Fiddle, the popover worked correctly, positioning itself in relation to the Select2's parent element.

What benefit came out of this was @Sparky's suggestion to use errorPlacement versus the method I was originally using, which gives more control over individual elements' error message placement methods.

I am currently investigating (and suspect) the culprit is an offcanvas plugin I'm using for a sidebar - I didn't include this in the Fiddle because I wanted to keep the Fiddle as concise as possible to the issue and not complicate things. I'll update this when I find out what the problem is. If anyone's interested in implementing this method for validation errors in Bootstrap 3 Popovers, I would use the code in the second Fiddle I posted.

Joyrex
  • 1,103
  • 14
  • 24
  • If anyone is looking for a more efficient and complete solution to this, see my other question that @Sparky helped answer - it's very educational and helps understand what the issue is here: http://stackoverflow.com/questions/35997816/using-bootstrap-3-popovers-with-ckeditor-4-and-jquery-validate?lq=1 – Joyrex Mar 16 '16 at 16:19