Exclusion Groups v4.0

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.

17 Responses to Exclusion Groups v4.0

  1. Tom McConnachie says:

    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

  2. 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

  3. Tom McConnachie says:

    Ahah!Very clever workaround.Many thanks,Tom

  4. Jono Moore says:

    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.

  5. Jono Moore says:

    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.

  6. Jono Moore says:

    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.

  7. Herve Dupriez says:

    Hi John,Have you considered how this could work with a XML schema data connection, where the exclusion group would be bound to a choice element?Or would you recommend another way to handle choice elements in dynamic forms, when each choice can be a complex type and require a subform?

  8. Herve:A choice element allows only one of a set of elements to exist under a containing element.You should be able to use one of these exclusion groups to represent a choice element as long as you carefully set up so that the unselected elements are null and that null values are not serialized. For details on null handling see:http://blogs.adobe.com/formfeed/2009/12/null_data_handling.htmlgood luck!John

  9. Vladimir Sych says:

    Hi John! I’m trying to use your validation framework in my forms. But i find some interesting thing: if fields are placed on many pages these fields not higlight, when i select it in listbox with error messages. Only fields which placed on one page with listbox are higlighted. Have you any ideas?
    Thank you!

  10. Vladimir Sych says:

    Hi John! I have found a new issue in your framework. I have a mandatory and a validation messages for some field. When i put some data in this field (field is filled now but not valid) message in message listener doesn’t change from mandatory to validation. Could you help me?
    Thank you!
    P.S. Sorry for my english &)

    • Vladimir:
      You are right. The problem is that the validation framework does not differentiate between mandatory and validation errors. Unfortunately, the fix is not trivial, so I can’t post one right now. I think the fix involves changing the storage of errors — store not only the SOM expression of the invalid field, but also the kind of error. Then when the kind of error changes you can make sure that notifyListeners() gets called.

      John

  11. Vladimir Sych says:

    Thank you! I have the same idea. But new problem was happened loops in dependency tracking :) Trying to resolve….

  12. Kirstine Kristensen says:

    Hi John
    Thanks for sharing your experience and knowledge with the rest of the world. I have used the code to make mutually excluded text fields and it has worked really well so far. However, now I have run into a problem when I have hidden sub forms containing exclusion groups, I can’t submit the form. The exclusion groups seem to be treated as mandatory even if they are not visible. Do you know a work around for that?
    Hope you can help me.
    Kirstine

    • John Brinkman says:

      Kirstine:

      Good point. Invisible objects should usually not be marked as invalid.
      You could add some logic so that objects that are not visible do not get an invalid status.
      I added this method to scValidate:

      function isVisible(vObject) {
        while (vObject.className !== “form”) {
          if (vObject.presence !== “visible”) {
            return false;
          }
          vObject = vObject.parent;
        }
        return true;
      }

      Then I added a check at the beginning of setStatus():

      function setStatus(vContainer, vStatus)
      {
      scGeneral.trace(“setStatus:”, vContainer.name, vStatus);

        // Don’t make invisible objects invalid
        if (!isVisible(vContainer)) {
          vStatus = true;
        }

  13. Evelyn says:

    Great post. I tried to use the Array method in my form. It worked at the first instance but not the second instance. Will this work when I have two subforms in the same document? One subform contain 4 checkboxes, and the other one contained 12 checkboxes. Each subform must form its’ own exclusion group. I copied the Variables and the “implemented as Array” subform from your sample, delete unwanted items and it worked for my subform with 4 checkboxes. I copied and pasted the subform, rename the subform, add more checkboxes, rename the checkboxes ( and change the name in the initialized script for the subform) but it won’t work. Help?

    • John Brinkman says:

      Evelyn:

      Hard to say where things have gone wrong for you.
      If I take the sample form and clone the first subform, both subforms continue to work fine. Even though they are using the same names.
      Check your javascript console and make sure you’re not seeing any errors there.
      good luck.

      John