Duplicating subform structures

This week I was asked to look at a problem where the customer wanted to have two sets of repeating subforms reference the same data.  With normal data binding techniques this doesn’t work. Data may be bound to only one subform. The solution in this case was to bind one set of subforms to the data, and synchronize a second set of subforms to the data using JavaScript.

In the attached sample, the first set of subforms is used for data collection, the second set is used to present the data as prose. In other words, we have two views of the same data: one for editing, one for final presentation. Of course, in many scenarios this would be done as two separate forms.  But we can make it work as a single form.

In the sample, the subform named "Me" is a hierarchy of subforms that binds to data that looks like:

<Me>
    <Fname />
    <LName />
    <BirthDate/>
    <Gender/>
    <Spouse>
        <Fname/>
        <LName/>
        <BirthDate/>
    </Spouse>
    <Dependent> <!– repeats –>
        <Fname/>
        <BirthDate/>
        <Gender/>
        <Hobby> <!– repeats –>
            <HobbyName/>
        </Hobby>
        <Pet> <!– repeats –>
            <PetType/>
            <PetName/>
        </Pet>
    </Dependent>
</Me>

The sample includes a set of repeating subforms that use "normal" (named-based) binding to connect to the data.  The second set of subforms and fields ("Summary") are declared with no data binding.  The Summary subform has a calculation script that synchronizes its descendent objects to the data. 

The calculation script iterates through the data under "Me", looking for fields and subforms that have the same name as the data. 

  • Where there’s a name match to a field, we assign the value. 
  • When there’s a name match on a subfom we recurse one level deeper into the hierarchy. 
  • When there’s a grouping element in the data (a dataGroup) and no corresponding subform, we look for an instance manager so that we can create a subform instance to match to the data. 
  • When there are optional subforms left over that don’t match data, we remove them. 
  • We keep track of which subforms and fields have been "bound’ to data so that the same objects don’t bind twice.
  • Because the logic is in a calculation, it will re-fire whenever the referenced data changes (due to dependency tracking)

There are other ways we could have synchronized data to a set of subforms, but this technique has some advantages:

  1. The logic is generic and can be applied to any form.
  2. We can add and remove fields/subforms without worrying about any bookeeping to match the objects to data.  The logic is centralized and will work as long as the form author uses names that match data.

The only caveat is that this solution won’t scale all that well to very large forms. The re-binding script runs every time a data value changes, and is looping through all the data nodes and form objects.  As the data set grows large, this logic will begin to slow down.