Validation Patterns: Part 2

Continuing from the previous post, we are looking at the problem of validating fields without inundating the user with message boxes during their form session in Adobe Reader — and without centralizing all the validation logic.

In the previous post, we established a design pattern for highlighting fields where a script validation fails. In this post we will deal with mandatory (required) fields.  Today, when you mark fields as mandatory, then any time your form is validated you will get an error message for each missing field.  We can do better.

The updated sample (with data) builds on the previous sample. The strategy is to

  • author the form fields using the standard XFA “required field” settings
  • At runtime disable the “required field” setting and store our own mandatory flag elsewhere in the field
  • Use our setStatus() function during validation to monitor whether fields are populated or not.

Changes from the previous sample:

  • Marked various fields as being mandatory
  • Additional logic in scValidate.setStatus() to cause empty mandatory fields to show up as invalid
  • A new script function: scValidate.setFieldMandatory()

Storing the mandatory state in a field involves a technique described in a previous post where we store a variable under a field.desc element.

Once we have logic in the form that stores our mandatory state, we let the XFA processor believe that no fields on the form are required. This way we don’t get any warnings from Acrobat/Reader, but we graphically modify the fields to highlight them for the user.

The only deviation from standard form design is that for mandatory fields you need to add a call to utility.setStatus() in the validation script – even if there is no script validation required.  i.e. if you mark a field as mandatory, you need to add this validation script:

expenseReport.details.empID::validate – (JavaScript, client)
scValidate.setStatus(this, true);

Conditionally Mandatory

On the sample form, when the payment type field choice is "Direct Deposit", there are three more fields that need to be filled in. If the payment type is any other choice, these fields do not need to be filled in.

Normally we toggle a field’s mandatory status by setting the property: field.mandatory. But now that our design pattern for mandatory fields has co-opted the XFA mandatory mechanism, we will use one of our new global methods: scValidate.setFieldMandatory(vField, vMandatoryState).

For this to work, the form defines validation scripts on the financialInstitution, branchNumber and accountNumber fields that look like this:


var vMandatory = directDeposit.rawValue == 1;
scValidate.setFieldMandatory(this, vMandatory);
scValidate.setStatus(this, true);

Updated Exclusion Groups

If you are looking at the script code you will notice that I have included the exclusion group script objects that were defined in a previous post.  The form has placed the direct deposit subform in an exclusion group with the other payment types. I have modified those scripts so that they work with the validation framework. (You will notice I have also broken them out into separate script objects with a new naming scheme).

The exclusion group sample has been updated so that:

  • When an exclusion subform has not yet reached its minimum number of entries, the subform gets highlighted instead of the individual fields in the subform.
  • The call to scGroup.setMinAndMax() now includes an optional validation message parameter.  This is needed because designer does not yet expose a UI for assigning a validation message to a subform.

Note that this sample now introduces the concept of an invalid subform.  If the user has not filled in all the credit card fields, then we mark the subform as invalid — similar to the case where the subform exclusion group did not reach its minimum number of entries.  The script to do this looks like:

form1.#subform[0].Payment.CC.CardType::validate – (JavaScript, client)
// Valid state is where either all fields are null or all fields are populated.
var vValid = (CardType.isNull  && CardNo.isNull  && Expiry.isNull) ||
             (!CardType.isNull && !CardNo.isNull && !Expiry.isNull);

// populate the subform validation message so that users get a meaningful message
if (this.parent.validationMessage.length == 0)
    this.parent.validationMessage = "All credit card fields must be filled in";

scValidate.setStatus(this.parent, vValid );

The validation button

The samples have also updated the logic for the button to validate the form.  Now when there are no errors, the button caption will be updated to say: "no errors" (and will be made read-only).  This works as long as you do not change the name of the button from : "checkValid".  You might prefer a variation on this where the button is hidden when there are no errors.

Backward compatibility

We would like to make it easier you to control validation message handling in a future version of Reader, but for the here and now, you are likely targeting Reader 7/8/9 and need an immediate solution.  You can take these script objects, copy them into new forms and use them in forms compatible as far back as Reader 7.

Next up: Another error reporting option

3 Responses to Validation Patterns: Part 2

  1. Bruce says:

    Thanks for the great info, I feel the fog thins a little everytime you have written.Could I ask for the preview data files to be added for the sample forms.Thanks again.

  2. Bruce:Glad to help. I have modifed this post to include the sample data as a separate download. I’ll try and make that a regular practise going forward.John

  3. > …> do you know the JavaScript Array initializer syntax?> var a = [“1”, “2”, …];I’m slowly learning :-)One of my more recent samples actually used the more elegant Array literal syntax.> … would there be a way to perform those same «picture» validations in script, somewhere else? I guess not…Picture formatting functionality is available in FormCalc — the parse() and format() functions.The hard part is to figure out how to connect a FormCalc script into the validation framework.Maybe that’s a topic for another blog entry.> The real problem of Host validation, appart from showing alert messages, is that one does not know, in script, if> a field or form is valid, or at least if the document as a whole is valid….I could live with the alerts…The sample form in the 3rd entry of the validation series exposes a function: scValidate.formHasErrors();> One other thing. I noticed you “store” every custom property as an xfa “variable” or as an “desc” or “extras” child.> I wonder if that is related to wanting persistence of those property/values or if you don’t know another way to> attach custom information to a field (or any other xfa object)?>> I’ve successfully created «expando» properties in any XFA object, storing there ANY JavaScript object I want.> These expando properties do not interfere with the data that is exported, submited, etc, it doesn’t show up. It lasts> as long as the document lasts:> MyField.__MyCustomProperty__ = [1,2,3,4,5];>> later on:>> app.alert( “[” + MyField.__MyCustomProperty__.join(“,”) + “]”)In fairness, I didn’t know about this capability until recently.But when I started poking around on this topic I got an earful from the development team.I posted a response in this blog entry:> A related question is: why do you keep the fields/forms somExpressions as keys, for later referral, in your listeners list?>> You could keep references directly to those objects, and not suffer of the somExpression-change problem you mention in> some places. I see one problem with this approach, we would be holding those objects in memory, which with dynamic lists> could be a problem. But we could always check if the reference we get from such a list of listeners is “alive”, by testing> its parent, for example?I needed a mechanism that worked reliably in every release back to Reader 7 without relying on “strict scoping off”.> I also noted that there is no xfa method of obtaining an object by it’s xml “id”. Which is a chame I think? And would simplify things here a lot.The problem with using the id attribute is that it is not unique in the form dom.For example a repeating subform can have an id of “s1” in the template, but when we merge it with data, there could be many “s1” subforms in the resulting form.Thanks for the comments!John