Acrobat User Community

Programming list and combo fields in Acrobat and LiveCycle forms - Part 1 of 2

By Thom ParkerSeptember 18, 2007

Scope: All Acrobat Full versions (not Standard) and LiveCycle Designer
Skill Level: Beginner
Prerequisites: Familiarity with Acrobat and LiveCycle forms

There are times when entries in a Combo box or List field need to be changed on the fly. For example, a parts order form might include two drop-down lists (combo fields); one for selecting an assembly and one for selecting a part within the assembly. Each time a different assembly is selected, the list of parts needs to be changed to reflect the correct assembly parts for that selection.
A different scenario is where individual items are added or deleted from a list, such as an e-mail distribution list. In this example, the form has two list elements: the master e-mail list and the distribution list. On the form, buttons are provided for both adding and deleting entries to/from the distribution list.
In all cases, manipulating list entries requires scripting. In this two-part article, we’ll explore programming options for both scenarios outlined above. In Part 1 (this one), we’ll learn about using list events and re-populating the list entries. In Part 2, we’ll learn about modifying list entries one at a time. Sample code for both Acrobat forms (AcroForms) and LiveCycle Designer forms (XFA forms) is provided in the article. Each of the example files contains more complete code than is presented in this article along with some bonus scripts for doing more advanced list manipulation.
General list operations
Before getting into the nitty-gritty details of each example, let’s take a higher-level view and look at some programming features shared by all list fields, regardless of forms technology. A List field, as the name implies, is a list of items sorted into some order. The user interacts with a List field by selecting one or more items from it. Typically, there will be a display string and an export value assigned to each list item. For both AcroForms and XFA forms, the export value is a string. Finally, a list should have some properties for controlling how it behaves.
While each of the forms technologies has its own diverse set of properties, two common list properties are a parameter for making the list single or multiple selection, and a parameter for controlling how item selections are committed. Committing is the process of taking the selections made by the user and applying them to the value of the field. This can be done when the focus is moved to another field, or immediately when the user makes a selection.
To manipulate a list programmatically there must be functions or properties to:

  • Add and remove items to and from the list
  • Get and set an item’s display and export values
  • Order the items
  • Get and set item selections
  • Set various list properties

Each forms technology has a different way of handling these features. Unfortunately, neither does a very good job of providing good functions for all the items listed above. However, both forms technologies do have enough support functions that it only takes a little extra coding to cover the holes.
Part 1- List Events, populating and clearing a list
Example files:

In this example, we’ll use a parts order form with two drop-down lists on each order line, as shown in Figure 1. (see page 1 in the example files). The first drop-down list selects the Assembly. The second drop-down is a list of parts for that assembly. Obviously, the parts list must change each time the Assembly selection changes.

Figure 1: Order form using two lists. Assembly selection changes entries on parts list.

In order to do this, we need a master list of lists. This master list must contain all the parts lists for each assembly type all the part prices. We’ll use an object literal stored somewhere in the document to accomplish this. The location where the master list is declared is different for each of the two forms technologies, but the list itself is the same.

var oAssemblyParts = {
	Chasis: [
		["-","None"],
		["Rear Bracket",205.95],
		["Front Bracket",185.95],
		["Long Support",44.95],
		["Front Bearing",48.95]
	],
	Clutch: [
		["-","None"],
		["Plate 1",15.95],
		["Plate 2",22.95],
		["Inside Shaft",44.95],
		["Outside Shaft",32.95]
	],
	Brake: [
		["-","None"],
		["Master Cylindar",139.95],
		["Slave Cylindar",85.95],
		["Pad",15.95],
		["High Presure line",22.95]
	],
	Coolant: [
		["-","None"],
		["Pump",35.95],
		["Thermostat",19.95],
		["Coolant Line",8.95],
		["Reservoir",17.95]
	]
};

Each entry in the object is a name/value pair separated by a colon “:”. The name is the Assembly name. The value is an array of the items for the parts list. Each item is small array containing the part name and the price. For example, use this code to get the item list for the “Clutch” assembly:

var clutchItems = oAssemblyParts["Clutch"];

To get the price of the “Plate” in the “Clutch” assembly, use this code:

var nPlatePrice = clutchItems[2][1];

We’ll also need a way to trigger the script that modifies the parts list. Each List field receives a number of events from Acrobat. There are events for Mouse Up, Mouse Down, On Focus and more. We’ll use the change event on the Assembly list to re-populate the parts list from the master list object. Both AcroForm and XFA Forms have this event and use it in approximately the same way. The change event is triggered when the user selects a list entry.
AcroForm solution
In AcroForms, the master list and all the supporting functions are placed in a Document Level Script. This is the standard location in an AcroForm for doing initialization and set-up activities.
The first supporting function is for setting the part entries. Fortunately, the master list contains the part/price information in exactly the right format for the function that populates a List field. The only other thing we have to be concerned about is handling the case where no Assembly is selected. In that case, we need to clear the list. Here’s the code:

function SetPartEntries() {
	if(event.willCommit) {
		// Get the new parts list from the Master List
		// Since the selection is being committed,
		// event.value contains the Assembly name
		var lst = oAssemblyParts[event.value];
		// Clear the Parts list if there are no parts for the selected assembly
		if( (lst != null) && (lst.length > 0) )
		this.getField("PartSelect").setItems(lst);
		else
		this.getField("PartSelect").clearItems();
		// We have a new parts lists and the first entry is
		// is a non-selection, so clear the price field.
		this.getField("Price").value = 0;
	}
}

Notice that the AcroForms model provides a single function for setting all items in the list. We simplified our programming task by formatting the master list to take advantage of this function, the “field.setItems()” function.
To use our “SetPartEntries()” function, simply call it from a the Keystroke Event of the Assembly list field, like this:

SetPartEntries();

The Keystroke event is called whenever the user changes the list selection, so in this case it’s really the Change event, which could be caused by a mouse action or by a keystroke.
The only other general-purpose function we need is one to handle populating the Price field when a part is selected:

function SetPriceValue(){
	// In order to get easy access to the export value, i.e. Price. This
	// function needs to be run on the un-committed change event.
	// This field is set up for Commit on select so we know the value
	// will be committed anyway.
	if(!event.willCommit){
		// If the export value is Not a Number then
		// set the price to zero.
		var nSelExp = 0;
		if(!isNaN(event.changeEx))
		nSelExp = event.changeEx
		this.getField("Price").value = nSelExp;
	}
}

In AcroForms, there are two kinds of Change events: Committed and Uncommitted. The difference between these two is more important for Text fields than it is for List fields, but for this function, we specifically want the Uncommitted Change event because this event makes the export (i.e., price) value available to us through the “event.changeEx” property. This is a matter of convenience, since it would only take a few more lines of code if we were to use the Committed Change event.
To use the “SetPriceValue()” function, simply call it from the Keystroke Event of the Parts list field, like this:

SetPriceValue()

The full implementation for this example can be found in the example file, “ListProgramming_Part1_AcroForm.pdf.” In this file, there are three order lines so the two general-purpose functions (SetPartEntries() and SetPriceValue()) are written in a more generic way to work with any of the drop-down lists in any line. This is done with creative field naming. Always pay close attention to how you name form fields. There is a direct relationship between the names you choose for the fields and how easy it is to program the form.
XFA-form solution
In an XFA form, the master list and support functions are placed in a Scripting object. The Scripting object can be attached to any sub-form in the form hierarchy, but the best location is on a parent sub-form close to the fields it will be working with, i.e., the parent of the order row, which is where it is located in the example file, “ListProgramming_Part1_XFAForm.pdf.”
There are several advantages to using LiveCycle Designer for this example. The number one reason is that no special code is required for handling multiple order rows like there was in the AcroForms example. Each row is in its own sub-form and the code only needs to reference fields in the same row. Another advantage is that rows can be added to the form dynamically.
Unfortunately, the list programming model for XFA is not that great. There is no function for populating the List field with all new entries like there is in AcroForms. The entries have to be added individually. Here is what our function for setting the part entries looks like in an XFA Form:

function SetPartEntries(){
	// Since entries are added one at a time it is necessary to
	// clear out the list first.
	PartList.clearItems();
	// The ComboBox value is not dependent on the list selection
	// so this also has to be cleared.
	PartList.rawValue = null;
	Price.rawValue = 0;
	// Grab the list of parts from the Master list and loop
	// over it to add entries into the Parts List Field.
	var aParts = oAssemblyParts[xfa.event.change];
	if(aParts && aParts.length){
		for(var i=0;i<aParts.length;i++){
			PartList.addItem(aParts[i][0],aParts[i][1].toString());
		}
	}
}

In XFA JavaScript, it is the "xfa.event.change" property that holds the text for the new selection. Another difference between this code and the AcroForm code is the parts list and the price field have to be explicitly cleared.

To use this function, simply call it from the Change event of the Assembly list, like this: SetPartEntries(); To populate the price field, we use a function very similar to what was used in the AcroForm code, with one main difference. In XFA JavaScript, the “boundItem()” function is used to retrieve the export value from the selection text. Here’s the code:

function SetPriceValue(){
	// If the export value is null or not a number
	// then set the price to 0.
	var newPrice = 0;
	newPrice = PartList.boundItem(xfa.event.newText);
	if(isNaN(newPrice)){
		newPrice = 0;
		Price.rawValue = newPrice;
	}
}

To use this function, call it from the Change event of the Part List field, like this:

SetPriceValue();

The full implementation for this example can be found on page one of “ListProgramming_Part1_XFAForm.pdf.”
Summary
In this article, we learned how to use the Keystroke event and the event object to instantly capture the user’s selection on a Combo Box field. This was done for two different Combo Box fields. In one case, the selection was used to re-populate the list on another Combo Box. In the other case, the selection was used to set a field value.
In Part two of this article, we’ll delve deeper in to the mysteries of List fields, taking a look at list sorting and how individual items can be added to or deleted from a List or Combo Box field.
1 Acrobat JavaScript Reference
http://www.adobe.com/devnet/acrobat/
2 XFA Object Reference
http://partners.adobe.com/public/developer/xml/topic.html