XFA 3.0: Validation State Change Event

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:


or via their shortcut properties:


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;


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.

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


3 Responses to XFA 3.0: Validation State Change Event

  1. Keith Gross says:

    I was doing a little testing about mandatory field handling and am getting some results I don’t quite understand. I set up a very simple form with a single field. That field is mandatory and has a validationState event coded directly on it that simply does a console.println(this.errorText);

    This work fine if I enter something in the field and then empty it but I was more interested in the other times mandatory field tracking starts. So I added some code to the initialize event on this field and placed the following statements in there and then checked if the validationState event indicated the field as being in error due to being empty.

    xfa.form.form1.execCalculate(); // No error
    xfa.form.execCalculate(); // error triggered
    form1.execCalculate(); // No error
    this.execCalculate(); // No error
    xfa.form.recalculate(true); // error triggered

    Based on your what you said in your post about when mandatory tracking begins I really expected more of these to show the mandatory field error. Thinking that having the code in the initialize event was part of the problem I also tried placing these in the click event of a button. This required that I alter the fourth line to use the field reference rather then this. The end results though were the same as if the code was in the initialize event.

    I’m also curious why visiting the field isn’t enough to trigger tracking of mandatory validation for the field? Once you’ve visited the field it would seem you’ve had your chance to fill the field and we should inform you that the field is mandatory.

    • Keith:
      It is really only execValidate() that triggers the start of mandatory validation processing.
      I suspect what happens here is that execCalculate() will trigger explicit validation processing only if it is executed against the form model.
      It also appears that calling execValidate() in the initialize script is too early. initialize events happen while the form model is being built. It looks like the validation state gets set at the completion of the form model construction (after the initialize events have fired).
      So if you want mandatory validation processing to start at form open, the best place to put the execValidate() call is in the docReady event.

  2. Keith gross says:

    I thought perhaps initialize was to early which is why I did the experiment a second time with a button and the click event. But got the same result with click as I did with I initialize.