Recently in mandatory Category

Script Validation vs. Null Validation

| No Comments

There is one question I get asked frequently enough that it warrants its own blog entry.

Someone tries a validation script that looks like this:

this.rawValue !== null;

But the validation never fails.  In spite of the fact that the field is null, this field always reports back that it is valid.

The reason is because of this rule: "empty/null fields never fail a script validation".  The reason behind the rule is that we want to allow designers to define validations without worrying about null as a special case.  Take an example:  the user needs to enter a value in a 'Age' field where the value must be greater than 18.  The way to express this is with this validation on the Age field:

this.rawValue > 18;

In an empty field this evaluates as "null > 18;" -- which evaluates to false.  Until the user enters a value, this validation script will always return false.

But allowing a script validation to fail on an empty field would be a lousy user experience.  As soon as they open a blank form they would get a pile of invalid fields -- even before beginning to enter data.  What should a form designer do?  They could change their script to explicitly allow null:

(this.rawValue === null) || (this.rawValue > 18);

But now there's the problem that we really do want to make sure they enter a value.  i.e. the experience we want is that the field starts out in a valid state.  Don't complain about script validations until they've actually entered a value in the field -- but also don't let them submit their data until the field has been populated with a value!

Of course, the answer is to make the field mandatory (the null test validation).  By making the Age field mandatory we know that the user will not be hassled as soon as they open the form; but they also won't be allowed to submit until they've provided a value.  And the form author doesn't have to handle a null value as a special case.

-----

Gone Again

I see Merriam-Webster has added "Staycation" to their dictionary.  Sounds good to me.  I will be away for another week at the family cottage.  I'll be be back to respond to comments on the 27th or 28th.

XFA 3.0: Validation State Change Event

| No Comments

The field, exclGroup and subform elements have a new event: validationState.  The primary use of this event is to change the appearance of fields as they become valid or invalid.

Just for review -- there are three ways in which objects can be considered invalid:

  1. A mandatory field/exclusion group that does not have a value
  2. A field with a value that cannot be formatted with its validation picture format
  3. A field/subform/exclusion group whose validation script returns "false"

Rules controlling the validationState Event

The validationState event fires under the following conditions:

  • Immediately following the initialize event -- allowing the form author to control the initial appearance of the object
  • When the field/subform/exclusion group transitions between a valid and invalid state (or vice versa).
  • When the reason for the invalid state changes e.g. a field is in an invalid state because it is not filled in (missing mandatory).  The user then fills in a value that violates the validation picture clause.  In this case the field goes from one invalid state to a different invalid state and the event will fire.
  • The form session begins tracking mandatory fields

The last point needs a bit more explanation.  When a field is marked as mandatory, it starts the form session in a valid state -- even though it does not have a value.  The reason is that we don't want users notified about a whole bunch of invalid empty fields as soon as they open a blank form.  There are 3 things that will put a missing mandatory field into an invalid state:

  1. A populated mandatory field gets emptied
  2. A call to execValidate() on the field or one of its ancestors
  3. A submit action (which is really the same as an execValidate())

When any one of these three operations happen, the validationState event will fire for any unpopulated mandatory fields.

To be on the safe side, script associated with the validationState event should be robust enough to allow the event to fire multiple times -- even if the validation state hasn't actually changed.

The new errorText property

So the validationState event has fired... how does the script author know whether the field is now in a valid or invalid state?  Authors can check the new property on field/subform/exclusion group: errorText.  When a field is in a valid state, this property will return an empty string.  When in an invalid state, it will return the user-defined message for that error. 

Objects may have 3 different user-defined messages -- one for each kind of validation failure.  These messages are represented in the scripting model as:

field.validate.message.scriptTest.value
field.validate.message.formatTest.value
field.validate.message.nullTest.value

or via their shortcut properties:

field.validationMessage
field.formatMessage
field.mandatoryMessage

The errorText property will be populated with the message from the associated validation failure.  If the user has not defined a message, a default message is provided ("Validation failed.").

Turning off Acrobat/Reader Field Highlighting

If you are using the validationState event to change the appearance of fields, the automatic field highlighting of Acrobat/Reader can get in the way.  Fortunately, it can be turned off.  This script (placed on a form:ready event) will do the trick:

    if (xfa.host.name == "Acrobat")
       app.runtimeHighlight = false;

Example

This example will hide the border on valid fields and for invalid fields will colour the border red.  Note that the example places the script on a subform and uses event propagation to cause it to apply to all nested objects.

<subform>
  <event activity="validationState" listen="refAndDescendents">
    <script>
      if (HasValue($event.target.errorText)) then
        $event.target.edge.presence = "hidden";
      else
        $event.target.edge.presence = "visible";
        $event.target.borderColor = "255,0,0"
      endif
    </script>
  </event>
</subform>

 

XFA 3.0: Message Handling Options

| No Comments

Of the XFA 3.0 enhancements, it's safe to say this one was overdue.  Today XFA processor in Acrobat/Reader notifies users about invalid fields by popping up dialog boxes.  However, form authors feel so strongly about wanting control over the notification mechanism that they develop elaborate JavaScript frameworks to avoid using the message boxes associated with the built-in validation/notification mechanism.

To put the flexibility back in the hands of the form author, XFA 3.0 has a new configuration setting to control how errors are handled:

<acrobat>
  <common>
    <validationMessaging>
        noMessages              |
       
firstMessageOnly        |
        allMessagesIndividually |
        allMessagesTogether

    </validationMessaging>
  <common>
</acrobat>

Don't worry about remembering the syntax or where it goes -- Designer will have a UI for setting this option.
The impact of this setting is pretty self-explanatory -- but I'll explain anyway:

  • noMessages - Acrobat/Reader will not emit any messages for invalid fields.  It will be up to the form author to notify the user in some way -- likely by changing the appearance of the invalid fields.

  • firstMessageOnly - When there are multiple errors on the form, Acrobat/Reader will display a message box for the first error and will suppress messages for all subsequent errors.  This prevents the user from being inundated with a long series of messages -- one from each invalid field.

  • allMessagesIndividually -- (default) This corresponds to today's behaviour.  Well, not quite.  There are instances today where we combine messages -- for example messages for missing mandatory fields.  But combined messages don't happen as much as you might like.

  • allMessagesTogether -- Aggregate all the messages from all invalid fields and emit a single dialog with all messages combined.

The sequence of validation script execution remains the same no matter which messaging option is chosen.  If the user attempts to submit when there are still missing or invalid fields, the submit operation will cancel with a message box.

XFA 3.0: presence="inactive"

| 2 Comments

This is the first in a short series of posts to describe the new functionality found in XFA 3.0.

If you have developed a form with even modest complexity, you have almost certainly manipulated the presence property of fields, subforms and exclusion groups.  With XFA 3.0 we have extended the possible values of this property to include "inactive".

The existing presence attribute can be one of these properties:

  • visible - (default) Object is visible
  • invisible - Object is invisible, but still participates in layout and event processing.  i.e. space is allocated in the layout to display this object, its calculations and validations still fire.
  • hidden - Object is invisible and excluded from layout.  No space is reserved for this object. Its calculations and validations still fire.

In XFA 3.0 we now add:

  • inactive - Object is hidden and excluded from event processing.  Calculations, validations and other events do not fire.

Scenario

A mortgage application form is sent to a client. The form has an optional section where the user may apply for mortgage life insurance.  The life insurance application section is pre-populated with client information from the server.  If the user chooses not to apply for life insurance:

  • the optional section will remain hidden
  • The calculation and validation scripts in the optional section will not fire
  • Other event scripts such as layout:ready, form:ready, prePrint, preSubmit etc. do not fire
  • We do not check for mandatory fields

All these behaviours will be true as long as the subform that defines the life insurance application is set to presence="inactive".

Once the user checks the box to apply for insurance, we toggle the setting to presence="visible" and then that section of the form becomes fully functional.

Containers, Properties

The new behaviour for presence applies only to these container objects: subform, field, exclGroup.  The presence attribute is also found on draw, fill, items, edge, border, caption and corner -- however in these contexts it will not introduce any new behaviour.  It will behave the same as "hidden".

The Deep End

In a previous post, I described the various stages of processing we go through when we open a form:

  1. Loading content
  2. Merging data with template (create Form DOM)
  3. Executing calculations and validations
  4. Layout (pagination)
  5. Render

The enumeration of the presence attribute determines which of these stages a form object will participate in:

  1. Loading content
  2. Merging data with template (create Form DOM) (visible, invisible, hidden, inactive)
  3. Executing calculations and validations (visible, invisible, hidden)
  4. Layout (pagination) (visible, invisible)
  5. Render (visible)

Validation Patterns: Part 3

| 2 Comments

Here is the last (for now) in the series on how to validate templates in a user-friendly manner.  I have attached a new and improved sample (with data).

What is new in this version:

  • An email submit button that becomes active only when there are no errors on the form
  • A listbox field that shows all the errors on the field.  When you enter into the list field, the field corresponding to the error gets highlighted.  Try selecting different elements in the list.

But more importantly, this sample form now holds a toolkit of functions that allow you to take control of the way Reader validates form fields.

Behind the scenes on the script side, there is a fair bit more in the toolbox.  My goal is that you can re-used these script objects in your forms and keep your form designs as clean and simple as possible.  If you look at the form you will see that the vast majority of script is inside the script objects.  The fields and subforms on the form itself have minimal amounts of script.

An added benefit of following this particular form development methodology is that is should be very consistent with future enhancements we add to XFA and Reader.

Here is a summary of the script functions that you can use in your form development:

/**
* setStatus(vContainer, vStatus)
* Call this method as the last line of your validation script.
* This function does two things:
* 1) highlights or resets the field according to whether it is valid or not
* 2) If invalid, logs the error
*
* @param vContainer -- the container we are validating. 
*                      If a subform, process recursively.
* @param validStatus -- true | false
* @return the status for the validation script to use 
*         (for now hardcoded to "true")
*/

/**
* formHasErrors()
*   Useful for changing the state of form objects depending on if there are
*   validation errors or not.
*   Place this call inside a validation or calculation script and make sure
*   you have called registerListener(this) from your initialization event.
* @return true if there are errors.  False otherwise.
*/

/**
* registerListener(vListener)
*   Call this from you initialization event if you want to be able to
*   call formHasErrors() in your calculation or validation events.
* @param vListener -- The object that cares about whether there are errors. 
*   We'll keep track of all the listeners. Every time a field changes valid
*   state (becomes valid or invalid), we will loop through all listeners and
*   force their calculate and validate scripts to run.
*   If a listener is a choiceList object, we'll synchronize the choicelist
*   entries with field errors.
*   When a choicelist has zero entries, we hide it.
* @return void
*/

/*
* setFieldMandatory(vObject, vMandatoryState, vForce)
*   Mark a field or exclusion group as being mandatory.
*   When calling this from a validation script,
*   be sure to also call setStatus()
*
* @param vObject -- the field or exclusion group that we're marking
* @param vMandatoryState -- true or false 
* @return boolean -- true if the state changed
*/

/**
* clearErrList()
* Call this method before removing a subform or re-ordering subforms.
* Our tracking is based on storing SOM expressions.
*
The SOM expression for a field can change if the order of its parent
* subform changes. 
* After clearing the list and removing/moving subforms,
* call xfa.form.execValidate() to rebuild the list.
* Note that it is not necessary to call this method when appending new subforms.
*/

Then there are a couple of methods you might choose to modify for your own use -- in case you do not like the way the sample highlights invalid fields:

/** 
* highlight(vObject)
*   Highlight a field (or exclusion group or subform) to indicate that it
*   has a validation error
* @param vObject -- the subform/field/exclusion group to highlight
*/

/**
* unhighlight(object)
*   reset the form field/subform/exclusion group to the state it was in
*   the template.
* @param object -- the subform/field/exclusion group to highlight
*/

 

The one thing that does not work back to Reader 7 is the button script that sets focus on an error field.  xfa.host.setFocus() came later.

But the rest of script functionality should work fine in Reader 7. The hardest part about making the scripts work in Reader 7 was that the convenience methods for manipulating choice lists are not available there.  The script has to manipulate the XML structures directly. 

About this Archive

This page is an archive of recent entries in the mandatory category.

layout is the previous category.

variables is the next category.

Find recent content on the main index or look in the archives to find all content.