Exclusion Groups v3.0

This is the third and final post describing a pattern for building your own exclusion group.  The first two posts are required reading for this entry:
Build a better exclusion group   Exclusion Groups v2.0

The included sample builds on version 2 and adds a couple of useful pieces of functionality:

  • Allow the exclusion group to have multiple entries selected
    (specify the minimum and maximum selected entries)
  • Make the exclusion group mandatory
  • Make the sample work in Reader 7

The Sample

Go ahead and open the attached sample. There is a new exclusion group, and for completeness I have also included the previous two examples.  When you open the sample, turn on field highlighting so that you can see when fields are mandatory (highlighted with a red border).  The sample shows a case where the user needs to select a minimum of one phone feature and a maximum of three.  Note these behaviors when you fill the form:

  • On opening the empty form, the fields are all highlighted red (mandatory) because none are selected — we are below the minimum selected.
  • When you select one entry, the mandatory setting is turned off. 
  • When you get to three selected entries, the remaining unselected entries are made read-only in order to prevent the user from selecting too many. 
  • If any entries are subsequently unselected, then the read-only setting is removed.

Code Changes

Initialization

There is now an initialization script on the exclusion subform to establish the minimum and maximum entries:

utility.setMinAndMax(this, 1 /* min */, 3 /* max */);

When users drags this object in from the object library, they need to tweak the initialization script to get the settings they want.  Note that if they set the maximum to one, then we use the toggling behavior when selecting entries (selecting one entry unselects another).  If they set a maximum greater than one we use the explicit select/unselect ui experience.

New functions

The script includes three new functions setMinAndMax(), setReadOnly() and setMandatory() plus new logic in makeExclusive() to set the mandatory and read-only states of exclusion group members.  From the behaviors described above and the code comments you can probably figure out how they’re used.

isSelected()

The logic inside isSelected() needed to be changed in order to support Reader 7.  Previously we relied on the selectedIndex property to determine if a checkbox was selected.  When selectedIndex is zero, the field is considered on/selected.  Grammar-wise, this is represented by this markup:

<field>
  <items>
    <text>yes</text>
    <text>no</text>
  </items>
</field>

selectedIndex refers to the position in the current chosen element in the <items> array. Since selectedIndex was not introduced until Reader 8, the alternate (Reader 7 compatible) strategy is to compare the value of the checkbox field to the first element under <items>.  If they are equal, the field is selected.

What Else?

I debated whether to expand on the logic behind isSelected().  For numeric fields, you might consider a value of zero to mean "not selected".  Also, there is probably more that could be done to support mandatory logic inside nested subforms.  For now I will leave these as an exercise for the user :-)

Summary

This is probably a good time to issue a caveat emptor.  These samples are intended to encourage some specific design patterns.  They are not supported objects for you to immediately use in your production forms.  You need to adapt them to your own needs and you need to test them in your own environments.

The main points I hope you take away from this exercise:

  • You can package your desired exclusion group experience into library objects without requiring users to add code to individual entries in the group
  • Tweak the provided sample to your own needs and include it in your object library
  • Centralize the script logic — do not have more than one copy in your form