Jack Jack - 3 months ago 18
Javascript Question

JQuery Validator Checkbox onClick update error message inconsistent behavior?

I've created a pretty standard checkbox JQuery Validation example: https://jsfiddle.net/rbL62zm8/1/

For clarity,

console.log
statements have been omitted from code in this question.

The validation
rules
require a Number or String type be selected. If a Number is selected, a numeric type must also be selected (Int, Float or Double).

var validationRules = {
debug: true,
rules: {
'type': {
// Number or String
required: true,
},
'numeric': {
// Int, Float or Double.
required: '#typeNumber:checked'
}
},
messages: {
'type': {
required: 'A data type is required.'
},
'numeric': {
required: 'A numeric data type is required.'
}
},
onclick: function(element) {
$(element).valid();
},
submitHandler: function(form) {
$("#validationMsg").html("The form is valid.");
return false;
},
errorPlacement: function(label, element) {
$("#validationMsg").html(label);
},
}


The corresponding HTML form:

<form id="mainForm" name="mainForm" method='post' action=''>
<input type="checkbox" name="type" class="type-group" id="typeNumber" value="Number">
<label for="typeNumber">Number</label>
<input type="checkbox" name="type" class="type-group" id="typeString" value="String" checked>
<label for="typeString">String</label>
<br />
<input type="checkbox" name="numeric" class="numeric-group" id="numericInt" value="Int">
<label for="numericInt">Int</label>
<input type="checkbox" name="numeric" class="numeric-group" id="numericFloat" value="Float">
<label for="numericFloat">Float</label>
<input type="checkbox" name="numeric" class="numeric-group" id="numericDouble" value="Double">
<label for="numericDouble">Double</label>
<br />
<input type='submit' name='Submit' value='Submit' />
<input type='reset' name='Reset' value='Reset' />
</form>

<div id="validationMsg"></div>


The form's default state ensures the String checkbox is ticked:

enter image description here

I've added an
onclick
implementation in an attempt to validate the form whenever a checkbox is ticked or unticked.

Use Case #1


  • Run the JSFiddle.

  • Untick the String checkbox.



Outcome: An error message is displayed, which is correct.

Use Case #2


  • Run the JSFiddle.

  • Untick the String checkbox.

  • Tick the String checkbox.



Outcome: An error message is displayed and remains visible. This seems inconsistent since the action that displayed the error message (un-ticking the checkbox) is reversed, yet the error message is not updated.

Question:
How can I validate all checkboxes whenever their state changes?




Update



I implemented the
invalidHandler
in the validation configuration object by adding:

invalidHandler: function(form, validator) {
var errors = validator.numberOfInvalids();
console.log('Number of invalids: %s', errors);
},


JSFiddle: https://jsfiddle.net/xz1a43fm/

The
invalidHandler
function is only called when the form's in an invalid state. Therefore ticking the String checkbox takes the form into a valid state and
invalidHandler
is not called.

submitHandler
is the closest thing to the counterpart of
invalidHandler
I can find - however this is called only
onSubmit
and not
onClick
.




Final Solution



I had three requirements:


  1. Realtime Validation: Validate checkboxes as the user's interacting with the form, not only at submission.

  2. Two-way Dependency: Validate two-way dependencies. I.e. if
    Number
    is ticked,
    Float
    ,
    Int
    or
    Double
    MUST be ticked, but also, if
    Float
    ,
    Int
    or
    Double
    are ticked,
    Number
    MUST be ticked.

  3. Don't Hack: Don't re-write built-in rules in a large custom validation method.



Realtime validation: Implementing a custom
onclick
function is common, however I was missing two crucial details:


  1. Validating the entire form, rather than a single element. By calling
    valid()
    on the entire form, all inter-dependencies between checkbox groups could be assessed.

  2. Checking for a valid state and clearing the error message manually if required (as suggested by @ZirboFilip).



The final
onclick
handler:

onclick: function(element) {
// Validate the entire form.
var valid = $('#mainForm').valid();
// Clear error message manually if appropriate.
if (valid) {
$("#validationMsg").html('');
}
}


Two-way Dependencies:
The required attribute is great for one-way dependency, but it isn't right for checkbox group dependencies. A reasonable rule may be:

'type': {
required: true,
},
'numeric': {
required: '#typeNumber:checked',
}


However this only states that a numeric type is required if ANY type (Number or String) is checked. Consequently the following is valid:

enter image description here

...this is not what I wanted. I added a custom validation method:

jQuery.validator.addMethod("checkDependencies", function(value, element) {
// return true if field is ok or should be ignored.
var intChecked = $('#numericInt').prop('checked');
var floatChecked = $('#numericFloat').prop('checked');
var doubleChecked = $('#numericDouble').prop('checked');
var numberChecked = $('#typeNumber').prop('checked');
var stringChecked = $('#typeString').prop('checked');
var numericTypeChecked = intChecked || floatChecked || doubleChecked;

if (!numberChecked && stringChecked) {
return !numericTypeChecked;
}
// Fall-back on built in rules for everything else.
return true;
});


I'll discuss this in more detail below while addressing my third requirement.

Don't Hack: I had only one additional requirement not covered by the built-in rules and dependencies, i.e. that String is not a valid type with when a Int, Float or Double is chosen. I coded this in a custom rule (code above). Relevant code repeated below:

if (!numberChecked && stringChecked) {
return !numericTypeChecked;
}
// Fall-back on built in rules for everything else.
return true;


However, after this check, I return
true
. While it was tempting to code other combinations in the same rule, by returning
true
, I continue to use the
required
rules, namely:

'numeric': {
required: '#typeNumber:checked',
}


Final JSFiddler: https://jsfiddle.net/bmg2dcha/1/

Answer

Add this:

if($(element).valid()){
    ("#validationMsg").html("");
}

inside onclick: function(element) { ... } and it should be fine.

Comments