Form Validation

Jaffa uses Validatious 2.0 to provide a flexible validation system with minimal effort. Whether you are using widgets to take care of the validation or bolting them on to basic form controls this will make validating your forms a lot simpler.

It is worth noting however that Jaffa's usage of Validatious focuses on the core APIs, as opposed to the higher level functions provided provided by the DSL. These higher level functions are generally incompatible with Jaffa since they internalise and automate data and actions that Jaffa would either like access to, or to replace with its own features. For all that, the core API's do provide a lot of options, as this page will demonstrate.

Basic Text Examples

These fields show basic examples of validation, although in this case they can be triggered from the button provided. This is simply to separate them from the rest of the form in this demo.

 

This demo uses built in Validatious rules(required and email), and we tell Jaffa to apply them like so:

jaffa.valid.setSaveRules("requiredText",  ["required"]);
jaffa.valid.setSaveRules("requiredEmail",  ["required", "email"]);

This method has additional optional parameters we've ignored however, such as callbacks. These are explored further below under 'Custom Validation'.

Triggering Validation

Internally, Jaffa is keeping a list of all Validatious rules we have provided against each field, along with the context (ie. 'save' or 'submit'). So we can request validation execute against specific fields and contexts at any time we want.

Top-level validation is typically built into the 'save' and 'submit' events firing, but you can target them explicity if desired:

Those two methods are really just cosmetic wrappers around the first two below (for the sake of convenience), but if you call them as per below, you can additionally provide an optional field name (or list) and just test against that field(s). Some examples:

The last entry is what we are running for the button below, which triggers the 'submit' context for validation, rather then the 'save' context we used above. The next section covers the additional custom validation that was added on 'submit'.

One final point to triggering validation is to see how we can control the behaviour of standard change() events on each field. By default all field changes will trigger validation in a 'submit' context, but this could either be disabled altogether by adding validateOnChange: false to your Jaffa config, or you could change it to a 'save' context using validateOnChangeType: "save". In either scenario, a field failing validation does not prevent the change from occurring, nor does it stop any change() callbacks you have specified from firing (although is does provide an isValid parameter to said callback).

This field is required to say 'Test' for submission. Editing it and failing to add this value will be detected on change.

Validation Failure: This value needs to be 'Test'.

Custom Validation

Jaffa doesn't treat custom validation as anything special, so adding your own custom logic is as simple as creating a custom Validatious validator (there's a mouthful) and telling Jaffa to use it like any other validator.

// A custom Validatious validator
v2.Validator.add({
    name: "myCustom",
    acceptEmpty: true,
    fn: function(field, value, params) {
        // Field value must equal first parameter (if it has one)
        if (params[0] == null || value == params[0]) {
            return true;
        }
        return false;
    }
});
// Add some rules using the above validator
jaffa.valid.addNewRule("myCustomFooBar", "myCustom", ["FooBar"]);
jaffa.valid.setSubmitRules("requiredText", ["required", "myCustomFooBar"], null, validationFail);

This is a very basic custom validator, but it is worth pointing out the acceptEmpty option because of its significance. The default value is true and it will cause your validator to 'pass' when the field is empty (ie. your validator will not execute, it will just pass). The Validatious documentation covers this (quoted below), and the reasoning is good, but it can cause the appearance of incorrect validation if you are not aware of it.

"acceptEmpty: Decides if the validator should pass (return true) when the value is empty. This is usually a good idea because you can leave it up to the 'required' validator to specifically check for emptiness. One benefit of this approach is more fine grained error reporting, helping the user."

Aside from the validator itself, we have a couple of other new things in this code snippet. First is the call to addNewRule(), which is used to tell Jaffa that we'd like to run a validator with some configuration (ie. parameters at call time). This can used whether or not you have a custom validator, but if you don't want to pass any parameters along you can just put the name of the validator directly into the call to setXXXXRules() (like we did earlier); Jaffa will work out what you mean.

The other thing shown above is connecting it to Jaffa in a 'submit' context. You'll notice that the setSubmitRules() call also has more parameters than we saw earlier (listed in order):

  1. "requiredText" the field we are validating, nothing new here.
  2. ["required", "myCustomFooBar"] an array of 'rules' to run for this field.
  3. null this parameter is for a callback if the validator passes, although obviously we aren't using it in our example.
  4. validationFail this parameter is for a callback if the validator fails and is where the demo opens message boxes (see below).
// A callback for validation failures
function validationFail(fieldId, testsFailed) {
    if (fieldId == "requiredText") {
        if ($.inArray("required", testsFailed) != -1) {
            jaffa.ui.messageBox("The 'Really Important' field is mandatory!");
        }
        if ($.inArray("myCustomFooBar", testsFailed) != -1) {
            jaffa.ui.messageBox("For submission, the 'Really Important' field must be 'FooBar'!");
        }
    }
    if (fieldId == "requiredEmail") {
        if ($.inArray("required", testsFailed) != -1) {
            jaffa.ui.messageBox("An email address is mandatory!");
        }
        if ($.inArray("email", testsFailed) != -1) {
            jaffa.ui.messageBox("Your email address is malformed!");
        }
    }
}

The example callback above isn't particularly elegant and is probably a bad way to do things for anything other then a small sample of fields. In practice you'll probably want the callback to alter your page's UI and place the error information in close proximity to your control. Also, unlike our demo, this scenario is where you would make use of the success callback (which we've ignored) to clear the UI up again. Jaffa's default widgets all use this approach, as does the demo above triggered by edits. Here's everything related to that field's validation (although we reused the earlier myCustom validator with a new rule). You can see we use Bootstrap classes to achieve the styling:

<div class="control-group thumbnail" id="validOnChangeDemo">
    <p>This field is required to say 'Test' for submission. Editing it and
        failing to add this value will be detected on change.</p>
    <label class="control-label" for="requiredTest">Test:</label>
    <div class="controls">
        <input class="jaffa-field span5" type="text" id="requiredTest" size="40" />
    </div>
    <div id="validOnChangeDemoMsg" class="alert alert-error">
        <b>Validation Failure:</b> This value needs to be 'Test'.
    </div>
</div>
$("#validOnChangeDemoMsg").hide();
function validationOnChangeFail(fieldId, testsFailed) {
    $("#validOnChangeDemo").addClass("error");
    $("#validOnChangeDemoMsg").show();
}
function validationOnChangePass(fieldId, testsPassed) {
    $("#validOnChangeDemo").removeClass("error");
    $("#validOnChangeDemoMsg").hide();
}
jaffa.valid.addNewRule("myCustomTest", "myCustom", ["Test"]);
jaffa.valid.setSubmitRules("requiredTest", ["required", "myCustomTest"], validationOnChangePass, validationOnChangeFail);

One final example here shows a basic proof-of-concept implementation for multi-field validation. Unfortunately the Validatious or() implementations don't work with Jaffa, since it restricts itself to usage of just the core APIs. So to implement this we create a custom validator, but then dip back into Jaffa to interrogate the form during validation. Finally, we attach the validator to both fields, so it will fire when either of them is edited.

This time we are looking at a custom validation spanning two fields. One of the two fields below must contain 'Test' for the validation to pass.

Validation Failure: One of the values above needs to be 'Test'.
// A custom Validatious OR validator
v2.Validator.add({
    name: "myCustomOr",
    acceptEmpty: false,
    fn: function(field, value, params) {
        // Get the values from Jaffa
        var orField1 = jaffa.form.value(params[0]);
        var orField2 = jaffa.form.value(params[1]);
        return orField1 == params[2] || orField2 == params[2];
    }
});
jaffa.valid.addNewRule("myCustomOrDemo", "myCustomOr", ["requiredTestOr1", "requiredTestOr2", "Test"]);
jaffa.valid.setSubmitRules("requiredTestOr1", ["myCustomOrDemo"], validationOnChangeOrPass, validationOnChangeOrFail);
jaffa.valid.setSubmitRules("requiredTestOr2", ["myCustomOrDemo"], validationOnChangeOrPass, validationOnChangeOrFail);