Exclusion Groups v4.0

| 6 Comments

December 5: Happy Sinterklaas! I hope your children did not find a lump of coal in their boots this morning.

I received some good feedback from the “build your own” exclusion groups introduced in a series of previous posts (part 1, part 2, part 3). These exclusion groups have enriched functionality not found in standard radio button groups.

Since then it has been pointed out that the strategy of using a subform to contain the group is not always practical. In particular there are two cases where using a subform does not work:

  1. PDFs with fixed pages (artwork PDFs). In this case, Designer does not allow you to add subforms to the form.
  2. The case where the exclusion group is spread across cells in a table. In this case, it is not possible to wrap the cells in a container subform. We cannot use the row subform, since there are other cells in the row that are not part of the exclusion group.

The new sample provided here allows you to create an exclusion group by providing an array of fields (or subforms) to include in the group. To make this possible, I have added two new methods to the scGroup script object:

/**
* addArrayGroup - Create an exclusion group by providing an array of
* containers (fields/subforms).
* Call this method from the initialization script of a field/subform.
* Note that a single initialization script can create multiple
* groups.  Once the groups have been initialized, you need to call
*
checkGroups() from the calculate event.
* @param vHost - The field or subform hosting the array group.
* @param vName - a name for the group (unique within this host)
* @param vList - An array of fields or subforms that will make up
*                the group
* @param vMin  - The minimum number of elements in the group that
*                need to be selected
* @param vMax  - The maximum number of elements in the group that
*                may be selected
* @param vValidationMessage - The message to use if the minimum
*    number of objects have not been selected
*    (and if the object does not already have a validation message))
*/
function addArrayGroup(vHost, vList, vMin, vMax, vValidationMessage)

/*
* makeArrayExclusive - Enforce the min/max constraints of an
* array-based exclusion group.
* @param vHost -- the container object that hosts the exclusion group
* definition.  
* Note that this will check all groups that this container is hosting
* In order to use this, you will need to call addArrayGroup() from
* the initialization event. This call needs to be placed in the
* calculation event of the same object.
* @return true
*/
function makeArrayExclusive(vHost)

The first example places these calls in the initialize and calculate events of a subform.

The second example adds a hidden field to the table to act as the host. The initialize event of the field calls scGroup.addArrayGroup() to define three groups:

form1.NPSA3.NPTable.Row1.groupControl::initialize - (JavaScript, client)
scGroup.addArrayGroup(this,
                      new Array(secondLine_yes, secondLine_no),
                      0, 1);

scGroup.addArrayGroup(this,
                      new Array(newCustomer_yes, newCustomer_no),
                      0, 1);

scGroup.addArrayGroup(this,
                      new Array(Bundle_yes, Bundle_no),
                      1, 1, "Must select yes or no.");

The calculate event calls scGroup.makeArrayExclusive() to enforce the exclusive behavior:

form1.NPSA3.NPTable.Row1.groupControl::calculate (JavaScript, client)
// Make sure the exclusion group behaviour is enforced
scGroup.makeArrayExclusive(this);

Note that if you create a lot of these on your form, it becomes expensive to clear the error list and recalculate everything. Fortunately you should not have to do that too often – just when removing subforms or when inserting subforms in the middle of a list. In these cases we need to clear/recalculate in order to update all the SOM expressions we have stored.  Have a look at the script in the subform remove buttons for an example.

The Deep End

The set of script objects that support validation and exclusion groups continues to grow in size and complexity.  By this point I expect most form developers would not be comfortable modifying them, but would want to treat them as a 'black box' library.  This is fine.  As far as I know, they continue to work back to Reader 7.0. 

To implement exclusion groups as arrays, I created a JavaScript base class with two derived implementations -- one for cases where the group is hosted by a subform and one where the group is controlled by an array.  Along the way I also fixed a couple bugs that were uncovered once I started placing the groups inside repeating subforms.

This will not be the last time I enhance these libraries.  I have some more ideas for them.  But that would be a different blog post.

6 Comments

Hi,

Firstly, thank you very much for your posts on exclusion groups, they have been very useful!

I'm using the code from part 3 to create a subform with multiple check boxes. I am also using the execValidate() function to check that all the mandatory fields in a parent subform are filled in before progressing to another subform. However this is not calling the validation on this exclusion group and I don't know how to get it to work.

Could you help me please?

Many thanks,

Tom

Tom:
Excellent question. I tried the same operation and ran into the same issue you're having. Turns out the problem is that Reader doesn't enforce mandatory checkbox fields. If you think about it, it makes sense, since saying a checkbox is mandatory is sort of like saying "you must check this field". But of course in our scenario we just want them to check one (or more) out of a set of check boxes.
Fortunately there is a workaround. Try adding a hidden text field to your exclusion subform. Mark it as mandatory and give it a nice explanatory message.
This hidden field will participate in the exclusion logic, and will be enforced by Reader (even though it is hidden).
I have updated the sample from my part 3 post to show how to do this.
John

Ahah!

Very clever workaround.

Many thanks,

Tom

Hi John,

Been playing around with this script on some simple yes/no checkboxes and I get a "vErrorListeners is not defined 274:1" error on opening and when clicking on one of the checkboxes. The script runs ok besides the error message.

All I'm trying to use at the moment is the scGroup.makeExclusive() function.

I've got a subform with 2 checkboxes in it with "scGroup.makeExclusive(this);" on the calculate event and setMinAndMax() set to 1 and 1 on the Initialize event.

Turns out it was a problem with fragments.

I had made the script objects into fragments for re-use. I made the fragments by dragging John's script objects onto the Fragment Library and that seemed to cause some problems.

I made the fragments again by right-clicking on the script object and going to Fragments->Create Fragment and that seems to have fixed the problem.

Actually it turns out that it wasn't a problem with fragments, it was with duplicated subforms of the same name that had the default sequential numbering (subForm[0] etc.).

Problem is fixed in a later version of John's scripts.

Leave a comment

About this Entry

This page contains a single entry by John Brinkman published on December 5, 2008 10:27 AM.

Form Design Code Review was the previous entry in this blog.

Adventures with JavaScript Objects is the next entry in this blog.

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