Validation Patterns: Part 3

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. 

9 Responses to Validation Patterns: Part 3

  1. An says:

    Hi,I’m working on a form that could possibly use XFA 3.0’s new visibility option “inactive”, but because most of the users at my company are still using Reader 8.0, I need to find an alternative way to use the setMinandMax function.Here’s my problem: I have a question (question #1) that toggles another subform’s visibility if the radio button list is selected yes. When selected as yes, the next subform will toggle from hidden to visible. I want to make question #2 mandatory with a minimum of 1 button (yes/no) selected before the user can submit anything. However, when I use the default code in my initialize event “scGroup.setMinAndMax(this, 1 /* min */, 1 /* max */);” even with an if statement, it refuses to recognize that this code should only fire if the if statement is true.I’ve also tried to resolve this problem with the scripts below, which seems to function perfectly, but it annoys me that the subform is not highlight in red – only the radio button list group is.—– form1.set1.page1.q6.acknowledgement.question2.q::calculate: – (JavaScript, client) ———–if (typeof(scGroup.makeExclusive) == “undefined”)xfa.host.messageBox(“You need to include the scValidate script object in your form”);else// Make sure that only one child is selected.scGroup.makeExclusive(this);—– form1.set1.page1.q6.acknowledgement.question2.q.RadioButtonList::validate: – (JavaScript, client)var vMandatory = form1.set1.page1.q6.acknowledgement.question1.q.RB2.Y.rawValue == 1;scValidate.setFieldMandatory(this, vMandatory);scValidate.setStatus(this, true);—– form1.set1.page1.q6.acknowledgement.question2.q.RadioButtonList.N::click: – (JavaScript, client)if (this.rawValue !=1) {app.alert(“If you have not discussed usage of funds with a unit, please do so prior to the submission of this Gift Agreement Request.”);}In other words, how can I get the setMinandMax function to work under an “inactive” situation without changing the presence to inactive?Thanks,An

  2. An says:

    Nevermind. I figured it out :)

  3. Loralon says:

    Hello John,

    I have a request for a form I have, with 3 email fields and all 3 have a validation script at the validation event. Now I would like to validate all 3 using only one script (function), can this be done, if yes how to do it?

    Thank you for in advance.

    • Loralon:

      You can centralize the code by putting it in a script object and have all three email fields call the function there. Currently there is no way to avoid have 3 separate validation events. But, at least they can be one-liners if they call the script object.

      John

  4. E. Loralon says:

    Thanks John,

    Would you kindly share a sample script that can be re-used? I am not a good coder, still learning.

    Loralon.

  5. Jan says:

    Hello John,

    your examples ( 2 and 3) are gone. Is there a way to get them?

    Jan

  6. Dale says:

    John:

    I like the concept of this. I’ve been doing some prototyping using this framework in order to assess it and have noticed a couple of things:

    1. If I fill in the Empty Message property on the Value tab, this causes a message box to pop up when the form initially renders. I tried adjusting the Form Level Validation properties but that made no difference.
    2. If a field is mandatory and also has validation constraints on it, the message box which appears after clicking the error button always shows the correct message (mandatory or validation failure message) but the error list ListBox does not update with the current message as appears in the message box.

    #2 is likely a small bug somewhere but #1 seems more systemic. Any thoughts on that?

    Dale
    2.