Stefan Cameron on Forms
Building intelligent forms using Adobe LiveCycle Designer

Multi-Selection List Schema Definition

After spending some time, recently, showing you how to connect your form to a schema and highlighting Designer’s support for schema metadata, I thought I would round-off my current train of thought on schemas by tackling multi-selection listboxes. Since they store their selected data in <value> nodes, once you think about it, their schema definition may not be obvious. When you’re working with XML data that isn’t governed by a schema and namespaces, it is perhaps easier to work with (i.e. accept) these <value> nodes however things change when you have a schema telling you how your data must be structured and scoped (with namespaces).

How Lists Store Selected Items

To better understand the challenges, let’s look at how list form objects store a multi-selection of values.

In the Form DOM, the list’s value actually contains XML rather than plain text. For example, say we have a multi-selection list named “ListBox1” (bound to a data element named “list1”) with items (“item1”, “item2”, “item3”). If the first two items were selected, the list’s value would look like this:

<value>
    <exData contentType="text/xml">
        <list1>
            <value>item1</value>
            <value>item2</value>
        </list1>
    </exData>
</value>

In script, this would translate to a newline (\n)-delimited string when the list’s value is accessed from its rawValue property:

item1\nitem2

In the Data DOM, the list’s value is stored in a series of repeating <value> nodes, much like the way it is stored in the Form DOM:

<list1>
    <value>item1</value>
    <value>item2</value>
</list1>

Adding the Schema

A schema describes how XML should be structured. Given how lists store their selected items in data, a schema that includes a multi-selection list will need to allow for the element that defines the list to contain zero or more repeating <value> nodes. Unfortunately, this means modifying your schema, if you already have one. (I can think of at least one workaround to this which would involve script and a sibling, hidden text field bound to the element in your schema, as opposed to the list itself, in which you would store the selection, but I won’t get into that in this article.)

For example, this is a schema that defines a “form1” root element that contains an element named “list1” that has a multi-selection list type (the way XFA expects it).

Data Connection

The problem with this structure is that once you connect your form to this schema, the Data View connection tree will interpret the list1 element as being a subform that contains a repeating node, “value”:

Because of this interpretation, you cannot simply drag & drop the “list1” schema element into your form in order to get a multi-selection list. Instead, you’ll get a subform with a text field inside of it instead (only an element with a simple type restricted to an enumeration of values would be interpreted as a list). Furthermore, you cannot use the picker widget next to the “Object palette > Binding tab > Data Binding property” to assign “list1” to a listbox you have already dragged into your form from the Object Library palette because “list1” is viewed as a container (subform) and the form object you are attempting to bind is a field.

Data Binding

In order to bind the listbox to the “list1” schema element, you must manually type its binding expression into the “Object palette > Binding tab > Data Binding property”. For our simple use case, the binding is “list1”. Note that you don’t actually bind the repeating “value” element to anything and that the schema data connection tree will not actually indicate that the “list1” element is bound to a form object even though it now is (I’m guessing this is because it is expecting “list1” to be bound to a subform, not a field).

The result of binding the listbox to the “list1” schema element is that the Form DOM output changes slightly to include the proper namespace on the bound element within the field’s value:

<value>
    <exData contentType="text/xml">
        <scof:list1 xmlns:scof="http://forms.stefcameron.com/ns/2009/">
            <value>item1</value>
            <value>item2</value>
        </scof:list1>
    </exData>
</value>

Submitting Data

As you might expect, given the structure of the listbox’s selection in the Form DOM above, the list’s selection data will be similar in the Data DOM (which is what would be submitted from the form as well):

<xfa:data xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
    <scof:form1 xmlns:scof="http://forms.stefcameron.com/ns/2009/">
        <scof:list1>
            <value>item1</value>
            <value>item2</value>
        </scof:list1>
    </scof:form1>
</xfa:data>

The only problem with the data above is that it isn’t valid against the schema which specifies that the repeating <value> nodes inside the list1 element are part of the “scof” (target) namespace and should therefore look like <scof:value>.

Submitting Namespaced Data

In order to get the namespace into the repeating <value> nodes for the selected values, we have to use a workaround that involves a little script in the listbox’s Exit event (though the script could be triggered elsewhere, if you like):

// JavaScript:

// schema namespace info
var nspace = "scof";
var nsUri = "http://forms.stefcameron.com/ns/2009/";

// get selection from list
var selection = this.rawValue.split("\n");

// build XML string for new selection in *Form DOM*
//  that includes the namespace in the <value>
//  nodes the outer <listSelection> node is
//  essentially irrelevant and will be discarded later
//  however it is required so that we can load its
//  content into the listbox's exData value node where
//  its value is set in the Form DOM
// this.dataNode.name gets the listbox's bound data
//  element name from the Data DOM which is required
//  in the XML
var newSelection = "<listSelection>\n<" + nspace +
    ":" + this.dataNode.name + " xmlns:" + nspace +
    "='" + nsUri + "'>";

// add selected items into the new selection -- take
//  note of the newline character at the beginning of
//  the string as it is critical to the XML loading
//  properly later on (it shouldn't be but it is...)
for (var i = 0; i < selection.length; i++)
{
    newSelection += "\n<" + nspace + ":value>" +
        selection[i] + "</" + nspace + ":value>";
}

// close the XML elements in the string
newSelection += "\n</" + nspace + ":" +
    this.dataNode.name + ">\n</listSelection>";

// load the new namespaced selection into the listbox's
//  exData value node, ignoring the root <listSelection>
//  node in the XML string
this.value.exData.loadXML(newSelection, true, true);

The script above essentially builds an XML string equivalent in structure to that which is normally used to store the list’s selection in the Form DOM however it adds the necessary namespaces to the repeating <ns:value> nodes. This change is then replicated in the list1 data node in the Data DOM and the form’s exported data changes to this (which can be successfully validated against the schema we’re using):

<xfa:data xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
    <scof:form1 xmlns:scof="http://forms.stefcameron.com/ns/2009/">
        <scof:list1>
            <scof:value>item1</scof:value>
            <scof:value>item2</scof:value>
        </scof:list1>
    </scof:form1>
</xfa:data>

Import Data Warning

Unfortunately, I have to point-out a bug as it relates to exporting multi-selection values with namespaces (which you only get when you use the above workaround script): Importing data with namespaced <ns:value> nodes for multi-selection lists — though valid XML and valid against the schema — will fail in Acrobat 9.1.3 (I have not done any testing on previous versions). My testing has revealed that the form doesn’t render properly after the data merge. Importing namespaced data that does not have namespaced <value> nodes for multi-selection lists, however, does work fine (this is the default way data is exported, unless you use the script I outlined above).

Sample Form

I have built a sample form that demonstrates how multi-selection lists store their data in the Form DOM and Data DOM. The form also includes a checkbox which, when checked, will generate namespaced <ns:value> nodes instead of the default <value> nodes, using the workaround script in the list’s Exit event.

Download:

Minimum Requirements: I designed this form using Designer ES2 Beta and Acrobat 9.1.3 however the form should work all the way back to Designer 7.1 and Acrobat 7.0.5.


Posted by Stefan Cameron on September 30th, 2009
Filed under Acrobat,Bugs,Data Binding,Scripting,Tutorials,XFA
Both comments and pings are currently closed.

6 Responses to “Multi-Selection List Schema Definition”

  1. Brad on October 28th, 2009

    Hi Stepan,

    I find your blog extremely helpfull.

    I’m a newbe to LiveCylcle (version 8.2.1.4029.1.523496). I have a simple dynamic form with one single DataConnection. On the form is a custom control data drop down list. I can populate the data drop down list via java script or using the binding tab (with dynamic properties turned on).

    The problem I’m having is that it takes an unreasonable amount of time for the query/dataconnection to run. In addtion, the form seems to want to requery every time the drop down control is selected. It virtually takes close to 4 minutes for the data list to repopulate every time.

    My current configuration is that the Data Connection is an ODBC connection to a system data source which points to a MS Acess Database on the client machine where LiveCycle also resides.

    However, I have tried using different ODBC DSN’s that connect to server databases, tried changing configurations where cashing is done on the server and/or client side, etc., etc. and I still have not been able to improve performance.

    Can you point me in the right direction?

    Thanks!

    Brad

  2. Brad on October 29th, 2009

    HI Stefan,

    Thanks for your blog – I’ve gotten so many useful tips over the last several months!

    Question for you. Is it possible for one list box to control what is displayed in another list box? Here’s the twist – both list boxes would be populated from an XML data connection. If so, do you have any examples you can show me to get me started?

    Thanks,
    Brad

  3. Stefan Cameron on November 2nd, 2009

    Brad (Oct 28),

    If you’re using the addItem() method to add items to the list, this could be the cause for the slow-down, especially if you’re putting a lot of data into it. My colleague John Brinkman has a great post on list performance using the newer setItems() method which might help-out.

  4. Stefan Cameron on November 7th, 2009

    Brad (Oct 29),

    Yes, you can certainly control what items appear in one listbox based on a selection in another listbox. To do this, you would load the first listbox based on the first data connection and the second based on the other data connection, though I’m assuming the second data connection wouldn’t also be a schema connection since you can only have one schema data connection per form. The other data connection would be either ODBC or a web service. My tutorial on selecting specific database records shows how to filter a second connection based on a selection in a list populated from the first connection.

  5. Duane on January 4th, 2010

    Hi Stefan,

    Having a problem with the List-Box issue which you address in this post.

    If I change the ListBox by de-selecting the “Allow Multiple Selection” checkbox, this script throws an error:

    listboxNode.value.exData has no properties

    For most of my code, I was using the workaround that you and John Brinkman had previously provided (which is similar), and it also fails with

    listboxNode.dataNode has no properties.

    Toggling multi-selection back on resolves the issue (but I need to limit to one choice). I tried exporting some data to see if the data is represented the same, and it still seems to be using the < value > tags, so I don’t understand why the dataNode would be null.

    Any ideas?
    Thanks
    – Duane

  6. Stefan Cameron on January 6th, 2010

    Duane,

    I don’t follow what you’re seeing. On my end, if I create a second list box (ListBox2) in my sample form and set it to be single-selection and add the following schema element for it to bind to:

    <xs:element name="list2" type="xs:string"/>

    I get the following output in the Data DOM (no <value> nodes for ListBox2):

    <xfa:data xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
       <scof:form1 xmlns:scof="http://forms.stefcameron.com/ns/2009/">
          <scof:list1>
             <value>item1
             <value>item2
          </scof:list1>
          <scof:list2>item2
       </scof:form1>
    </xfa:data>

    And I get the following output in the Form DOM (note that single-selection list objects have a <text> value type node rather than an <exData> value type node since their value is a simple string rather than a complex XML structure):

    <field name="ListBox1" xmlns="http://www.xfa.org/schema/xfa-form/2.8/">
       <value xmlns="http://www.xfa.org/schema/xfa-template/3.0/">
          <exData contentType="text/xml">
             <scof:list1 xmlns:scof="http://forms.stefcameron.com/ns/2009/">
                <value xmlns="">item1</value>
                <value xmlns="">item2</value>
             </scof:list1>
          </exData>
       </value>
    </field>
    [...]
    <field name="ListBox2" xmlns="http://www.xfa.org/schema/xfa-form/2.8/">
       <value override="1" xmlns="http://www.xfa.org/schema/xfa-template/3.0/">
          <text>item2</text>
       </value>
    </field>

    Note that this works without the “AddNamespace” checkbox checked. If it was checked, the same script on ListBox2 would fail since it doesn’t have an <exData> node — as you pointed-out. That being said, the scof namespace is already being properly applied to the <scof:list2> element in the data so there’s no need for the script in the single-selection list case.

    Are we on the same page or did I miss what you were getting at?