1

Using jQuery Validation plugin, I defined the following for my Bootstrap form:

$(".form-with-validation").validate({
    errorClass: "help-block",
    errorElement: "span",
    highlight: function(element) {
        $(element).closest('.form-group').addClass('has-error');
    },
    unhighlight: function(element) {
        $(element).closest('.form-group').removeClass('has-error');
    }
});

It works well for a simple form. However, the part with highlight and unhighlight doesn't work when a .form-group contains multiple inputs (inline) that need to be validated:

<div class="form-group">
    <label class="col-md-4 control-label">State &amp; Zip</label>
    <div class="col-md-3">
        <select class="form-control required" name="state">
            ...
        </select>
    </div>
    <div class="col-md-3">
        <input type="text" class="form-control required" name="zip">
    </div>
</div>

The problem is that once you select a state for example, the input becomes valid, and its .form-group parent loses .has-error class, even though the sibling input (i.e. zip) is still invalid (i.e. has a .help-block span below it):

enter image description here

So, I changed the unhighlight part to the following:

unhighlight: function(element) {
    var formGroup = $(element).closest('.form-group');
    var formGroupIsValid = true;
    formGroup.find('input').each(function(){
        if (! $(this).valid())
            formGroupIsValid = false;
    });
    if (formGroupIsValid)
        formGroup.removeClass('has-error');
}

Yet I get the following error:

Uncaught RangeError: Maximum call stack size exceeded

Any ideas why? I tried many approaches, but each time I get the same error.

EDIT

I'd prefer to stick with div.form-group having .has-error class if possible (because of styling).

EDIT 2

Jsfiddle to demonstrate the issue.

0

4 Answers 4

2

Here is the solution I ended up with. It was simpler than I thought. As people have indicated before, any form-group should contain only one form-control at a time. The easiest solution is thus to put a second form-group inside the first one and then place the second form-control in there:

<div class="form-group">
    <label class="col-md-4 control-label">State &amp; Zip</label>
    <div class="col-md-6">
        <div class="row">
            <div class="col-sm-6">
                <select class="form-control required" name="state">
                    ...
                </select>
            </div>
            <div class="col-sm-6 form-group" style="margin-bottom:0;padding-right:0">
                <input type="text" class="form-control required" name="zip">
            </div>
        </div>
    </div>
</div>

With just a few CSS styles, this works perfectly and looks just fine. Here's a jsfiddle.

Sign up to request clarification or add additional context in comments.

Comments

1

You are calling a function which calls another function and so on, until you hit the call stack limit. I am going to assume the problem is in your .each loop when you call .valid().

You shouldn't have to do any of that though. Instead of targeting form-group you should target something around the input specifically so you don't have to change that unhightlight function. For example something like:

<div class="form-group">
    <label class="col-md-4 control-label">State &amp; Zip</label>
    <div class="col-md-3 inputToValidate">
        <select class="form-control required" name="state">
            ...
        </select>
    </div>
    <div class="col-md-3 inputToValidate">
        <input type="text" class="form-control required" name="zip">
    </div>
</div>

And then update the JavaScript code to something like:

$(".form-with-validation").validate({
    errorClass: "help-block",
    errorElement: "span",
    highlight: function(element) {
        $(element).closest('.inputToValidate').addClass('has-error');
    },
    unhighlight: function(element) {
        $(element).closest('.inputToValidate').removeClass('has-error');
    }
});

2 Comments

I tried to stick with .form-group since if I target the closest div for example, my CSS styling would be messed up. But thanks for your input nonetheless!
@Alex Oh I see, you still want the red to show on the label too? Or are you talking about other css? You are still leaving form-group so other css should be intact.
1

I'd prefer to stick with div.form-group having .has-error class if possible

It's not possible with the options provided by the plugin. The valid/invalid classes are toggled on the element being validated. The highlight and unhighlight functions can be modified to toggle classes on other elements using jQuery DOM traversal, etc.

However, you want logic that makes a parent container "invalid" when any of its children are invalid... the plugin is not equipped for that. As soon as an invalid child element triggers the error class on the parent, any valid child element will apply the valid class to the same parent.

A workaround would be an external keyup and change handler that looks at the classes on all sibling input elements and toggles its parent class accordingly. Based on your own code and untested...

$('input, select').on('keyup change', function() {
    var formGroup = $(this).closest('.form-group');
    var formGroupIsValid = true;
    formGroup.find('input, select').each(function(){
        if (! $(this).valid()) {
            formGroupIsValid = false;
        }
    });
    if (formGroupIsValid) {
        formGroup.removeClass('has-error');
    } else {
        formGroup.addClass('has-error');
    }
});

I get the following error: Uncaught RangeError: Maximum call stack size exceeded .... Any ideas why?

Yes, you are calling the .valid() method from within the .validate() method (via unhighlight). So calling $(this).valid() from within this method only causes unhighlight to be called again... and so on indefinitely.

1 Comment

Thank you for your elaborate response. Unfortunately, the workaround did not work for me... I've created a jsfiddle to demonstrate this. Could you have a quick look at it please?
-1

You don't have to use the form-with-validation class you can target all the form field that you are wanting to validate by selecting the inputToValidate class. It also much simpler. Also you probably want to use the toggleClass.

$(".inputToValidaten").validate({
    errorClass: "help-block",
    errorElement: "span",
    highlight: function(element) {
        $(this).toggleClass('has-error');
    },
    unhighlight: function(element) {
        $(this).toggleClass('has-error');
    }
});

Updates and Edites

OK. So here is code that will work on any nested form field with the form-control class. It will go up and grab the closest element with the form-group class and then add you validation code to it.

$(".form-control").validate({
    errorClass: "help-block",
    errorElement: "span",
    highlight: function(e) {
        $(e.target).closest('.form-group').toggleClass('has-error');
    },
    unhighlight: function(e) {
        $(e.target).closest('.form-group').toggleClass('has-error');
    }
}); 

2 Comments

The OP wants to apply the error class to the parent if any one of its children is invalid... this is very different than what you're showing us.
And the way you've setup .validate() here with these options, you can only attach it to a single form element, not a class of inputs. It just doesn't work like that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.