Posts in Category "validations"

Customize a Validation Experience

This is the second of four exercises that were part of the LiveCycle best practices lab at MAX

Implement Field Validation

Open EX2 Validation.pdf in Designer

  • Preview and click the “Submit by email” button to validate the form
  • Notice:
    • Errors are reported with message boxes. 
    • Selecting the error message in the listbox highlights the error field
    • Tabbing out of the listbox will set focus to the selected error field.
  • Change the form properties to customize error handling to: “Don’t show any message boxes”
  • Modify the (propagated) validationState event under the root subform:
    • On error: color fields and modify the toolTip value
    • Else: revert field color and toolTip
  • Bonus:
    • Add a change event to the phone number to restrict entry to digits

The solution file is: EX2 Validation (Solution).pdf

Reverting changes to fields:

To write re-usable code for changing values back to their original state, we should never hard-code the changes. E.g. we should never revert a color by writing: Field.fillColor = "255,255,255". The original color may not have been white.

Rather, after modifying an element, we can revert by removing the changed element. Removing an element forces the XFA processor to use the original value found in the form template.

E.g. Change the font color by modifying the font.fill.color element:
Field.font.fill.color.value = "255,0,0";

Revert the change by removing the color element:
Field.font.fill.nodes.remove(Field.font.fill.color);

Or by removing one of the ancestor properties:
Field.nodes.remove(Field.font);

Changing/filtering keystrokes

  • In the change event, xfa.event.change holds the contents of your key stroke (or paste buffer)
  • E.g. force all input to upper case:

    xfa.event.change = xfa.event.change.toUpperCase();

Validate image size

Let’s talk about image size (again).  But this time in the context of data capture. We have this wonderful image field that allows your users to attach image files to their form.  This is great. but it’s also scary.  You likely don’t want to use this for the 10MB images that came straight from their digital camera.  Consider that these images will be base64-encoded and inserted with the rest of the form data — and stored in the PDF.  You want to limit them to images that are reasonably sized. 

Today’s sample has an image field with a validation script that checks the size of the loaded image.  For an image field, field.rawValue will return the base64 value of the embedded image. The length of that string will tell you how big the image is. My validation script does a rough conversion — image size is roughly 3/4 the size of the base64 string. If that size is greater than your threshold you can choose to either reject the image (set the field to null) or simply mark the field as invalid.

Propagating Events

Back to another of the 9.1 enhancements intended to help you write less code: propagating events. See here for an overview.  Again, now that we have a Design tool capable of authoring these and now that the 9.1 Reader is more common, we should have a closer look. 

For starters, here is the sample form I’ll be referencing.  As you try the form you will notice a couple of behaviors:

  • as you move your mouse over fields, they are highlighted
  • invalid fields get a thick red border

The punchline here is that there are 14 text fields on the form, but only one each of a mouseEnter, mouseExit and validationState event scripts.  These events are defined on the root subform and apply to all descendent objects.  This is a great technique for minimizing code in your form.  A highly recommended form design technique. 

But now the bad news… So far, this functionality has not been easy to get at through Designer.  In the ES2 designer there was a check box to make an event propagate.  But the script dialog didn’t allow field events to be defined in the context of a subform.  Then because of the confusion this functionality caused, the checkbox was removed from the Desginer that shipped with Acrobat 10.  This functionality should re-emerge in a future version of Designer. 

In order to make it easier to get at this functionality, I have written a macro that will add propagating events to subforms.  The UI appears like this:

After you run the macro, the event and script will be added to your subform. Now, the tricky part — to edit the script you need to do two things: 1) close/re-open the form. Yeah, macros are still beta, and Designer doesn’t pick up on the changes made by the macro. 2) on the script dialog for the subform, choose to show: "events with scripts".

One interesting thing to note is that it is valid to have two or more of the same event defined on a subform. e.g. you could have both a propagating enter event as well as a non-propagating enter event.

The Deep End

When changing the display of an object to show an error state, it’s a bit of a challenge to revert the object back to its original state.  e.g. If you change the border color or border thickness you need to know the original color and thickness in order to restore the original state of the field.

Well, not really.  You’ll see the validationState script in the sample uses a different technique to restore the original display.  The script removes the border and assist elements from the field.  To understand why this works you need to understand the relationship between the form and the template.  The template is the definition of a form you see in Designer.  At runtime, we merge the template objects with data and create form objects.  A form field is then a sparsely defined object that has a pointer back to a template field. 

When we execute script that changes the color of a border, we modify the form definition of the border and override the default border properties found in the template. Removing the border from the form field means that we have removed the override and the field then reverts to the definition found in the template.

 

A better validation pattern for 9.1 forms

Today I’d like to go back again to revisit some functionality introduced in Reader 9.1 (XFA 3.0).  In 9.1 we added some enhancements specifically designed to improve the form validation user experience — and to allow validations to happen with less JavaScript code.  Specifically:

  • Message Handling options (described here)
  • The validation state change event (described here)

Prior to these enhancements, users were avoiding the use of validation scripts because they didn’t like the message boxes that popped up on every validation failure.  But now we have control over those message boxes, and, as we’ll see, there are lots of good reasons to like using validation scripts.

First things first, turn off the message boxes.  There’s a new designer dialog for this:

Designer dialog box showing a setting where the author can specify that validations do not display message boxes.

Great! now we don’t get those pesky dialogs showing up every time.  But now, of course, it’s up to you to find some other way to convey to the user that they have invalid fields in their form.  This is where the validationState event comes in.  It fires when the form opens, and it fires again every time a field validation changes from valid to invalid or vice versa.  The expected use of validationState is to change the appearance of the form to flag invalid fields. 

One more important piece of the puzzle: field.errorText. When a field is in a valid state, this text is and empty string.  When the field is invalid, it is populated with validation error text.

Take the example where we expect a numeric field to have a value greater than zero.  The validation script looks like this:

encapsulation.A1.B.C.D::validate-(JavaScript, client)
this.rawValue> 0;

The validationState event will set the border color and the toolTip text:

encapsulation.A1.B.C.D::validationState-(JavaScript, client)
this.borderColor = this.errorText? "255,0,0" : "0,0,0";
this.assist.toolTip.value = this.errorText;

Setting the toolTip value is important for visually impaired or color-blind users who won’t notice a change in border color. (There’s another topic waiting on designing a good validation experience that works with assistive technologies).

Hopefully this looks pretty clean and easy to code.  It’s important to contrast this approach with the alternative — just to make sure you’re convinced.

The Alternative

The alternative to field-by-field validation is to centralize form validation in one big script.  The big validation script then gets invoked by some user action – usually a form submit or print or validation button. Working with the same example, this script would look like:

encapsulation.validate::click-(JavaScript, client)
var field = A.B.C.D;
var bValid = field.isNull || (field.rawValue> 0);
if (bValid) {
     field.borderColor = "0,0,0";
     field.assist.toolTip.value = "";
} else {
     field.borderColor = "255,0,0";
     field.assist.toolTip.value = "The value of D must be greater than zero";
}

Here is a sample form that has both styles of validation.  And here are some reasons why you should prefer field-by-field validation:

  • Less code.  Three lines of code vs. nine. In a form with hundreds of fields, this difference becomes compounded
  • Better user experience — immediate feedback when fields become valid or invalid
  • Form processor is aware of the invalid field and will automatically prevent form submission
  • External validation scripts need to also enforce null/mandatory fields.  In the field-by-field approach, mandatory is enforced without any script.
  • Encapsulation: A term from our object-oriented design textbooks.  In this context it means that the field definition (including business logic and messages) is self-contained.  The benefits of encapsulation include:
  • Notice that the second example references the field by it’s SOM expression: A.B.C.D; There are any number of edit operations that could change that expression: moving a field, unwrapping a subform, renaming a subform or field etc.  If any of these operations happen, the script will need to be updated.  In the first example, there is no SOM expression needed and the script can remain the same through all thos edit operations.
  • Use fragments.  If you want the field to be included in a form fragment, you need to make sure that the validation logic is included with the field.  When the logic is encapsulated, this is not a problem.  When the validation logic is outside the field, it’s much harder to find ways to have the logic accompany the field.

Not Ready for 9.1?

Assuming you’re now convinced that field-by-field validation is what you want, you might still be in a situation where you can’t assume your users have 9.1.  In that case, I’d encourage you to check out some of my older blog posts that included a script framework that allowed field-by-field validation without the 9.1 enhancements.  The most recent version of that framework was included in this blog post.

Understanding Field Values

Knowing a bit more about how field values are represented in JavaScript could make a difference in how you write script.  Today I’ll give an overview of how field values are processed.   For starters, here is a sample form that will illustrate some of the points I make below.

But first, we need to be mindful that picture clauses (patterns) impact our field values.  A brief recap of the various picture clauses (patterns) we use:

  • format pattern: Used to format field data for display
  • edit pattern: Used to format field data when the user has set focus in a field and is about to edit a value.
  • data pattern: Used to format field data when it is saved to XML.
  • validation patten: Used for field validation — does not impact field value

With that background, let’s look at the properties available:

field.rawValue: the field value in its raw form. i.e. a format that has no patterns applied and a format that is consistent across locales and is suitable for calculations.

field.editValue: The field value when formatted with the edit pattern. If there is no edit pattern you will get a reasonable default — usually the rawValue, except in the case of a date field where you’ll get some locale-sensitive short date format.  If a field value cannot be formatted using the edit pattern, then field.editValue will revert to be the same as field.rawValue.

field.formattedValue: Same as field.editValue except using the display pattern.

Some miscellaneous facts about field values:

  • field.editValue and field.formattedValue always return a string. If the field is null, these will return an empty string
  • rawValue returns a JavaScript type corresponding to the kind of field.  e.g. A text field is a string, a numeric/decimal field is a number
  • when a field is empty, rawValue will always be null
  • rawValue is what will be stored in the XML by default (assuming no data pattern has been specified)
  • There are two ways to check for a null value:
    • field.rawValue === null
    • field.isNull
  • The rawValue of a date field does not have a type date.  We chose to represent dates as a string — in the form that they will be saved.  The format used for the string is YYYY-MM-DD.
  • If you use JavaScript typeof to determine what kind of a value a field has, be aware that typeof(null) returns “object”

Knowing all this, there are some implications for the code you write:

  • If you need a JavaScript Date from a date field, you can use this function:
    function ConvertDate(sDate) {
       if (sDate === null) {
          return null;
       }
       var parts = sDate.split("-");
       // Convert strings to numbers by putting them in math expressions
       // Convert month to a zero-based number
       return new Date(parts[0] * 1, parts[1] - 1, parts[2] * 1);
    };
                
  • You should never code: field.rawValue.toString()
    This will result in a JavaScript error when the field is null.
  • If you want an expression that always returns a string, never null, use field.editValue or field.formattedValue
  • I often see code in the form:
    if (field.rawValue == null || field.rawValue == “”) { … }
    This is unnecessary.  Use one of the following:
    if (field.isNull) { … }
    if (field.rawValue === null) { … }
  • If you prefer not to use a validation pattern, you can validate a field using the display picture.  Use this validation script:
    this.formattedValue !== this.rawValue;

Optional Sections of Forms

When Adobe first released Reader 9.1 I wrote a series of blog entries describing the new forms features.  But of course, at that time it would have been easy to overlook them because you didn’t have a designer that could target 9.1.  Not to mention that your user base would take some time to upgrade to Reader 9.1.

But now some time has passed and it’s a good idea to revisit some of the 9.1 features.  Today we’ll look at field.presence = “inactive”.  For background, you may want to re-read the original post here

The sample for today shows a scenario where the form requests a payment type.  If the payment type is “credit card”, we need to expose the credit card fields to fill.  Not only that, but the credit card fields need to be mandatory. With the inactive setting, this becomes a very simple script:

if (this.rawValue === "Credit Card") {
    CreditCardDetails.presence = "visible";
} else { 
    CreditCardDetails.presence = "inactive";
}
            

If you wanted to do the same without the inactive setting, the script would look something like:

if (this.rawValue === "Credit Card") {
	CreditCardDetails.presence = "visible";
	CreditCardDetails.cardExpiry.mandatory = "error";
	CreditCardDetails.cardNumber.mandatory = "error";
	CreditCardDetails.cardType.mandatory = "error";

} else { 
	CreditCardDetails.presence = "hidden";
	CreditCardDetails.cardExpiry.mandatory = "disabled";
	CreditCardDetails.cardNumber.mandatory = "disabled";
	CreditCardDetails.cardType.mandatory = "disabled";
}

And this is with a very simple scenario where the optional section consists of three fields.  Imagine if the optional section had dozens of fields with mandatory settings, validation scripts etc.

The presence=”inactive” setting was added to help reduce the amount and complexity of code authors needed to write to author forms.  If you make use of it, you should find your forms easier to code and less difficult to maintain.

Use the change event to filter keystrokes

If you’re wondering why a sudden flurry of blog posts, it’s because I spoke at the Ottawa Enterprise Developer User Group meeting last week.  I prepared a bunch of material for that presentation, and now I need to make that material generally available.

I spoke a lot about validation techniques in form design.  Today I’ll focus on using the change event to validate user input. 

User input should be validated as early as possible.  Compare the experience between:

  1. Wait until the user submits the form — then highlight all the validation errors
  2. Validate as the user exits the field
  3. Validate input as the user types into the field

The earlier the validation happens, the better the user experience. Let’s look at what is involved in validating input as the user types into the field.

The change event fires every time the user enters data into a field.  The change is normally a keystroke, but could also be a delete or a paste operation. When the change event fires, there is lots of useful information available in the xfa.event object.  I’ll describe the properties that are relevant for today’s discussion:

xfa.event.change: the contents of the data being entered.  Normally this is the keystroke.  But it could also be the contents of the paste buffer.  Or in the case where the user hits the delete or backspace keys, it is an empty string. You can modify the value of xfa.event.change in the change event.

xfa.event.selStart, xfa.event.selEnd: Tells us where the change event will happen. "sel" is short for "selection".  selStart and selEnd are character positions.  When the user has selected text, they describe the range of selected text.  When no text is selected, selEnd will be the same as selStart and text will be inserted at that position.  You can change the values of selStart and selEnd in the change event.

xfa.event.prevText: The contents of the field before the change is applied

xfa.event.fullText: What the contents of the field will be after the change is applied.

Now, some practical examples of what you can do in the change event.  Here is a sample PDF containing all the examples.

Force upper case

If you want to make sure that the contents of your field will be upper case, then modify xfa.event.change like this:

xfa.event.change = xfa.event.change.toUpperCase();

Allow only numeric characters

If you create a field that is a numeric type, then Reader/Acrobat will automatically restrict users to valid numeric input.  But suppose you’re gathering a telephone number or a credit card number. These are normally text fields that hold numbers. In this case you want to "swallow" any changes that insert non-numeric characters. This script uses a regular expression to test the change contents and cancel if necessary:

if (xfa.event.change.match(/[^0-9]/) !== null) {
    // swallow the change
    xfa.event.change = "";
    // if the user has selected a range of characters,
    // then leave the range intact by re-setting the start/end
    xfa.event.selStart = 0;
    xfa.event.selEnd = 0;
}

Visual Feedback

I once designed a form with a telephone number that accepted only digits.  I swallowed spaces, brackets, dashes and other formatting characters that the user entered.  Then I found out that a couple of the people filling in the form abandoned it because they couldn’t enter data in that field.  They needed some feedback that their keystrokes weren’t valid.  This next example temporarily sets the field border red and thick when the user enters an invalid key.  <deepEnd>The script uses the app.setTimeOut() method.  Notice that I call it from a script object.  If the return value of setTimeOut() gets garbage collected, the event will cancel. Variables declared outside script objects will be garbage collected.</deepEnd>

// If the user has entered invalid data, cancel the event and give some visual feedback
if (xfa.event.change.match(/[^0-9]/) !== null || xfa.event.fullText.length > 10) {
    // cancel the change
    xfa.event.change = "";
    xfa.event.selStart = xfa.event.selEnd = 0;
	
    // turn the border red and thick
    this.borderColor = "255,0,0";
    this.borderWidth = ".04in";
    // Turn the border back to black after one second
    var sRevert = 
        "var This = xfa.resolveNode('" + this.somExpression + "');\
        This.borderColor = '0,0,0';\
        This.borderWidth = '.02in';";
		
    helper.timer(sRevert, 1000);
}

Disable your form in older versions of Reader

When designing forms that require newer versions of Reader, you may want to be intentional about the experience users get when they open the form in older versions of Reader.  Today if you target a form for
Reader X and open it in Reader 9, you get this dialog:

After you’ve dismissed the message, Reader goes on to display the form as best it can.

The problem is that "as best it can" could lead to a very poor experience.  If you have functionality in your form that relies on a more recent Reader, then in all likelihood, your form is broken.  This could manifest itself in any number of ways.  e.g. Validations not firing correctly or the form not displaying correctly. 

As a form author, you’d prefer to prevent the user with an old version of Reader from having any interaction with the form.  No experience is better than a bad experience.  The topic for today is a design pattern to author a tailored experience for the user with an older Reader.

Design Pattern

There are probably several ways this can be done. I’ve chosen to add a version-checking subform below the root.  This version-checking subform has the following properties:

  • Contains content that warns the user that their version of Reader is not new enough
  • Points the user to the reader update page
  • Has a form field with the required Reader version number
  • Has an initialization script that compares the Reader version to the required version
  • If the Reader version is not new enough, the initialization script will remove instances of other subforms below the root and make sure the version checking subform is visible.
  • If the Reader version is new enough, the initialization script will add instances of other subforms below the root and hide the version checking subform

For this script to be effective, the other subforms below the root must be defined with occurence settings where the minimum is zero and the initial number is also zero:

I have attached a sample form that uses this technique. The form is targeted for Reader 10.  If you open it in version 9, you will get the version check warning experience.

Updated Accessibility Checker

I recently got some feedback that the accessibility checker I posted for Designer would fail for some forms.

I have updated the script to handle the condition that was causing it to fail.  The new version is in the updated Accessibility.zip download.   This time I also included the mxml source used to create the SWF dialog — just in case you want to customize it for your own use.

Meanwhile, I also added a check for a new error condition:
"Invalid next node reference: <ref>"

This error will appear in the case where the markup generated for node traversal contains an invalid SOM reference.

If you’re using this checker and have any ideas for improvement, please let me know.  It’s a high priority for us to enable customers to create accessible content.

Usage notes

A couple more notes about the checker, and macros in general:

  • When a macro issues a message, the message appears in the log tab of Designer. If the macro script fails for some reason, this is also where the failure message will appear
  • With the very latest Acrobat update, you might find that the report PDF gets launched with a yellow bar warning.  The only way to get past this right now is to turn off enhanced security In Reader/Acrobat.  Obviously we’ll have to figure out a better solution if/when this capability makes its way to a supported product feature.

Dependency Tracking

One of the really cool aspects of XFA forms is the dependency tracking mechanism.  Dependency tracking is the feature where your field (and subform) calculations and validation re-fire whenever any of their dependents change.  Today I’ll explain a bit about the mechanics of how dependency tracking is implemented as well as have a look at possible design issues related to this mechanism.

Simple Example

Suppose my purchase order form has order rows with fields: price, quantity and subtotal along with a total field.

The subtotal field has a JavaScript calculation: "price.rawValue * quantity.rawValue"

The total field has a FormCalc calculation: "sum(order[*].subtotal])"

Now whenever price or quantity fields change, the corresponding subtotal field will be recalculated automatically.
Whenever any of the subtotal field values change, the total calculation will re-fire.

Discovering Dependencies

When the form is first opened, all the calculations and validations are executed.  While executing a calculation or validation, the XFA engine keeps track of each object (field, subform, data node) that gets referenced during the script execution.  Each referenced object is added to its dependency list.  In our example, each subtotal field becomes dependent on the price and quantity fields from its row.  The total field becomes dependent on all the order subforms and on all the subtotal fields.  The mechanism is robust enough that even if you reference your objects indirectly, we still find the dependency.  e.g. the subtotal calculation could be written as:

parent.nodes.item(0).rawValue + parent.nodes.item(1).rawValue

Since the nodes we visited were still price and quantity, the dependencies are established just the same.

In some cases, the dependency list will grow as values change.  Consider this calculation:

if (A.rawValue < 100) {
    A.rawValue;
} else {
    B.rawValue;
}

Suppose the first time it executes, field A has a value of 20.  This means the code will not execute the else clause and will not access field B.  The initial dependency list will include only A.  However, if the A of changes to 200, the calculation will re-fire; the else clause will be evaluated and field B will be added to the dependency list.

If your calculation or validation calls a script object, dependencies will continue to be tracked during the execution of the script object method.

Dependent Properties

What constitutes a field change? What changes cause a calculation to re-fire?  I don’t have a complete list.  But changing a field’s visual properties (colours, captions, presence) do not cause a dependent to recalculate.  Changing the value or changing the items in a list box will cause a dependent calculation/validation to re-fire.

Turn off Automatic Calculations

If, for some reason, you’re not happy with the default dependency tracking, then you can turn it off. There are two switches to control calculations and validations independently:

xfa.host.calculationsEnabled = false;
xfa.host.validationsEnabled = false;

Note that turning validationsEnabled off disables not only script validations, but also turns off mandatory field and picture clause validations.

Dependency Loops

It is possible to get into a situation where sequence of dependency changes goes into an infinite loop. In these cases, the XFA engine will allow the calculation or validation to re-fire a fixed number of times (10) and then will quietly stop re-firing the calculation or validation until the next time a dependent changes.  While the 10 times limit is ok in most cases, I have seen forms where this has been the root of performance issues.  If you have a dependency loop and your calculations are re-firing, you want to be aware of it and you need to fix it. 

Dependency loops are typically introduced when a calculation or validation modifies another field.

I should highlight that statement.  If your calculation or validation minds its own business and never modifies another field you shouldn’t run into any loops.  There are lots of cases where a calculation or validation modifies another field and everything works fine — but tread carefully.

Looping example

In order to track when my validations were changing, I wrote validation scripts write to a debug field:

field T1 validation:

debugTrace.rawValue += this.rawValue;
true;

field T2 validation:

debugTrace.rawValue += this.rawValue;
true;

Both T1 and T2 now have a dependency on field: debugTrace

The sequence of events:

  1. Field T1 changes
  2. T1 validation fires and modifies debugTrace
  3. T2 validation fires and modifies debugTrace
  4. T1 validation fires and modifies debugTrace
  5. T2 validation fires and modifies debugTrace

Here is a sample form to illustrate this example.

Note that if we remove the validation from T2, the circularity stops.  The validation on T1 modifies debugTrace, but after the validation completes, debugTrace does not change and T1′s validation does not re-fire.