Validation Patterns: Part 1

Time to tackle my pet peeve. The aspect of customer written script that concerns me most is how customers validate field data. The number one reason for script bloat is that our users bypass the standard XFA/Reader validation framework.

Of course, there is a reason why script writers bypass validation. The problem is that each validation message appears in an individual dialog box. In some cases when a form is validated, the user may have to dismiss dozens of dialogs. Most users would prefer no dialog boxes at all – just show the invalid fields graphically. This is a problem we would like to solve in a product release, but in the mean time, you need forms that work in previous versions of Reader. Starting today I will tackle this topic in a series of blog entries.

Current Practise

Form authors currently work around the “validation message” issue by moving their validation logic outside the prescribed validation mechanisms. They typically place all the validation logic for their form under the click event of a “validate” button. They end up with a very large chunk of script that look like:

if (purchaseOrder.amount.rawValue >= 1000) {
     errorMessage += "Amount must be less than 1000\n";
     errorFlag = true;
}
if (purchaseOrder.quantity.rawValue == null || quantity.rawValue == 0) {
     errorMessage += "Quantity must be specified\n";
     errorFlag = true;
}
... repeat for each business rule ...
if (errorFlag) {
    xfa.host.messageBox(errorMessage);
}

The result is that field definitions are no longer encapsulated (self-contained). The problems introduced:

  • Fields cannot be copied/pasted/moved/deleted/renamed without breaking script
  • Adding new a field requires updating a global script – hard to correlate which fields have validations script and which do not.
  • Validation messages are stored and handled in script. Any attempt to isolate strings from the template for translation or spell checking is very difficult.
  • Validations happen only when the user explicitly asks for a validation – e.g. by clicking a button. They do not get incremental notifications.
  • Total amount of script is greater, and the script is more complex

Best Practise

Here is a sample form where:

  • Validation logic is placed in field validation scripts
  • Invalid fields are highlighted with a red border
  • No message boxes appear during data entry
  • There is a button that checks overall form validation status and if there are errors, places the user in the first invalid field

The solution has 3 parts:

  1. Global Script Objects
  2. Field validation logic
  3. Validation button

Global Scripts

There are six global scripts under the utilities script object. The ones that the form author should be aware of:

setStatus(vField, vStatus)

Called by field validation logic to handle the validation logic for a field.

highlightField(vObject)

Changes the appearance of a field (or exclusion group) to indicate that it is invalid. In this case we change the border color. Anyone who wants to re-use this mechanism can modify this script to get the visual effect they prefer.  Note that for this sample, the highlighting works best if fields do not have a raised border.  It also works best for dynamic forms.  It should be re-specified if used for a static form.

unhighlightField(vObject)

Reverts the field to the appearance defined in the template. If you modified code in highlightField(), you need to make the corresponding change in this function.

There are a three more scripts that are used internally:

registerFieldStatus(vField, vStatus)

Saves a list of invalid fields in a form variable.

getMessage(vObject)

Retrieves the validation message from a field object.

findTemplateField(vField)

Finds the template field that we use to reset visual properties.

Field Definition

Several fields on the sample form have validation logic. The general form of the validation script looks like this:

expenseReport.#subform[0].expenses.expense.amount::validate – (JavaScript, client)

// place field validation logic in the getStatus() method.

// Make sure it returns true (valid) or false (invalid).

function getStatus()
{     // place field validation logic here.

}
utilities.setStatus(this, getStatus()); // register valid/invalid state

Note the properties of this script:

  • The validation will fire every time the field (or any of its dependencies) change
  • Since the setStatus() function always returns true, the field is always valid according to the XFA processor.
  • The setStatus() function does the work to change appearances etc.

Other than the field validation script, the important aspect of this design is that the validation message is specified at the field level.

Validation Button

The validation button will:

  • Check the form variable holding the list of invalid fields
  • If there are invalid fields, extract the validation message from the first invalid field and issue a message box
  • Set focus to the first invalid field

Topic for next post: Handling mandatory fields.

10 Responses to Validation Patterns: Part 1

  1. Theodoros says:

    First, thank you for your wonderful articles! They are BIG help for all of us out here! John, I try to figure out how the red border for mandatory fields disappears on your sample form when users enters some data!How you can achieve this effect?Thanks for your help

  2. Theodoros:There is a sequence of events that happen:1) After modifying a field, the field validation script will run. The validation script will call the setStatus() method.2) When the setStatus() discovers that the field is now valid, it will remove the red border by calling unhighlightField().3) In various versions of the framework, unhighlightField() works differently. There are two methods I prefer:a) For dynamic forms, simply remove the border element from the form DOM This will cause the field to revert to template DOM values. e.g.F1.nodes.remove(F1.border);b) Explicitly set the border properties to be the same as the template (a better solution for static forms). The code in utilities.unhighlightField() in the sample of this page uses this technique.It’s important to note that every field has two borders! There is the border around the entire field (field.border) and there is the border around the data entry area (field.ui.oneOfChild.border).Note that static forms support only the latter.Good luck!John

  3. Theodoros says:

    Dear John, Thanks for your reply! Is it possible to send me a small sample to understand better the all situation.I am confuse with all these variables…Thanks for your time in this matter

  4. Theodoros:The blog entry above has a sample that does all this. From the “sample form” hyperlink above, save the PDF to your file system. Then option the file in Adobe Designer. Look at the validation script on the “receipt” field. It uses the functions defined in the script object named “utilities”.good luck!John

  5. Kevin McHale says:

    Your form crashes LiveCycle Designer 9.0.0.1. No surprise there; LC Designer crashes on average 4 times a day.

  6. Kevin:I had a quick look and wasn’t able to reproduce the problem in my copy of Designer 9.0.0.0I’ve forwarded on to the QE folk to see if they can do better.Meanwhile, you can help the team out by submitting bugs with any crashes you encounter. There’s a blog entry describing how:http://blogs.adobe.com/lcdesigner/2009/08/submitting_crash_information_u.htmlthanks,John

  7. Rich says:

    Hi, this looks like exactly what we need to solve our validation problem. Unfortunately the link to the sample form does not appear to work. Can this be found from an alternative source?

    • John Brinkman says:

      Rich:
      I think I’ve fixed the download issue. Let me know if there are any problems.
      Also be aware that this post is the first in a series of blog entries where I developed a validation mechanism. The most up to date version of this code is in the form-to-design-a-form entry where you originally commented.
      good luck.

      John

  8. Tim says:

    I tried retrieving the sample today and I get a 404 error. Could you please repost the file.