Working with Multiple Data Records

| 10 Comments

The XFA processor has a notion of data records.  A record is the data that populates one instance of your form.  If you are designing interactive forms, you have probably never had more than one data record in your form.  The notion of multiple records is primarily for processing large print jobs in LiveCycle Print.  Consider the case where you want to print customer statements.  You extract an XML file from your database with the data for all 2000 customers.  Then in a single call to LiveCycle Print you print all 2000 records.

I won't go into all the details of setting up a multi-record print.  My intention today is to show how you can leverage multiple record support in interactive forms.

Data Record

Suppose you have a form that binds to a root data node named: "form1".  When there is a single data record, your data looks like this:

<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
   <xfa:data
>
      <form1>...</form1>
   </xfa:data>
</xfa:datasets>

In the simple case, a form with three records will look like:

<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
   <xfa:data>
      <form1>...</form1>
      <form1>...</form1>
      <form1>...</form1>
   </xfa:data>
</xfa:datasets>

But by default Acrobat/Reader will display only the first record.  But with a few simple script commands you can navigate to different records as well as add and remove records.

Script Expressions

The Current Record
The current data record is easy to find.  We have set up a convenience property: xfa.record.

Record Count
xfa.record.parent.nodes.length;

Current Record Number
xfa.record.index;

Record Count
xfa.record.parent.nodes.length;

Strictly speaking, this will not always return the record count... but that's in the deep end.

Add a Record
var newRecord = xfa.datasets.createNode("dataGroup", xfa.record.name);
xfa.record.parent.nodes.append(newRecord);

Remove a Record
var vRecordToRemove = xfa.record.parent.nodes.item(xfa.record.index);
xfa.record.parent.nodes.remove(vRecordToRemove);

Goto a Record
xfa.datasets.dataWindow.gotoRecord(nRecord);

Sample

I have attached a sample that exercises all these script commands.

Exporting XML data

One thing you will notice when you export or submit data as XML, the result will change depending on if there is one or more than one record included.  From the example above, if I export as XML and there's one record I'll get:

<form1> ... </form1>

If I export a data with 3 records, we need to add an aggregating element so that it remains valid XML:

<xfa:data xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
    <form1>...<form1>
    <form1>...<form1>
    <form1>...<form1>
</xfa:data>

Changing the Current Record

You need to be aware that when you call dataWindow.gotoRecord(), the XFA processor will perform a re-merge with the new data.  Doing a remerge means that you lose any changes you've made to your form objects.  e.g. you will lose field highlighting, choice list definitions populated by script etc.

The Deep End

There are some advanced options for handling very large datasets on the server.  If you think about it, a large print job could contain many thousands of records, and the input could be a data file that is many gigabytes.  To handle that case there are options in configuration that allow you to process the file incrementally.  Part of that involves specifying a record element by level or by name.  e.g. you could say that the name of the record node is "customer".  In this sample XML:

<customerRecords>
  <jobDetails>...</jobDetails>
  <customer> ... </customer>
  <customer> ... </customer>
  <customer> ... </customer>
  <customer> ... </customer>
</customerRecords>

there are 4 records, but the <customer> parent element has 5 children.  That's why, strictly speaking, the expressions: xfa.record.index; and xfa.record.parent.nodes.length; are not always reliable.

But if you haven't set the config file options, then there's no problem.

Data Window

To round out the story of data records, we need to say a bit more about incremental processing -- commonly referred to as "lazy loading".  The idea is that the XFA processor holds only a small number of data records in memory.  As the XFA processor loads the data file it progressively loads data records into a window and when they've been processed they are discarded.  The window can specify how many records before and after the current record are kept in memory.  In this way we can process a multi-gigabyte data file without loading it all into memory.

As mentioned, there are configuration options for setting up the data window behaviour as well as a dataWindow scripting object.  But these details would need to be the topic of another blog entry.

10 Comments

Hi John,

This is very interesting! It reminds me of the support for multiple datasets, which would be another interesting post topic...

John,
I am not well versed in XML or XFA. I need a tutorial or example of how to populate two dropdown boxes from another dropdown box. First, I am not sure how to structure the xml doc correctly. Here is an example of the xml I am attempting to use:
<code>
<myinfo>
<country/>
<countries>
<item uiname="United States" token="US"/>
<item uiname="South Africa" token="SA"/>
<item uiname="Australia" token="AU"/>
</countries>
<state/>
<US>
<item>California</item>
<item>New York</item>
<item>Texas</item>
<item>Michigan</item>
<item>North Carolina</item>
<item>South Carolina</item>
</US>
<AU>
<item>Australian Capital Territory</item>
<item>New South Wales</item>
<item>Northern Territory</item>
<item>Queensland</item>
<item>South Australia</item>
<item>Tasmania</item>
<item>Victoria</item>
<item>Western Australia</item>
</AU>
<SA>
<item>Eastern Cape</item>
<item>Free State</item>
<item>Gauteng</item>
<item>KwaZulu-Natal</item>
<item>Limpopo</item>
<item>Mpumalanga</item>
<item>Northern Cape</item>
<item>North-West</item>
<item>Western Cape</item>
</SA>
</myinfo>
</code>
This xml will populate the first DD with the countries, which in turn populate the second DD with the states. Now I also want it to populate a third DD with different data. First DD should populate the other two DDs each with different data. Is this possible?

Here is the js I am using in the "change" event of the first DD.
<code>
myinfo.page1.country::change - (JavaScript, client)
var tempString = "xfa.record." + this.boundItem(xfa.event.newText);
var oItems = xfa.resolveNode(tempString);
var nItemsLength = oItems.nodes.length;
myinfo.page1.state.clearItems();
myinfo.page1.state.rawValue = null;
for (var nItemCount = 0; nItemCount < nItemsLength; nItemCount++)
{
myinfo.page1.state.addItem(oItems.nodes.item(nItemCount).value);
}
</code>

Can you help? thx

Jake:

I have posted a blog entry dedicated to this topic.
Have a look at the sample posted with this entry:
http://blogs.adobe.com/formfeed/2009/10/populating_list_boxes_1.html

Hopefully there's enough there so you can code by example.

John

Is it possible to populate a form from 2 different sources at the same time.

I can do one or the other at this time.
I have a form which needs to be populated with initial data from an xml file (the fields to be populated are like the sample PO dynamic form in the samples. It has a growing table.). Once it is populated I then want to merge it with different student information. thus creating multiple pages in one file with each page containing the information of the initial form merged with the individual student information. This is a report card - teachers then fill out the form for each student and it is printed.

Is there a way to merge the two processes into one? I've used Stefan's show all records in the initialize, which work great, but, I can't seem to get both to work in one form.

Any assistance is greatly appreciated.
Mo

Mo:

Any time we import data into a form we replace all the existing data. Unfortunately, there's no 'append' mode.

One approach is to control the input via script. Put a button on your form for importing the student information. In the click event:
1. save the current form data to a JavaScript variable
2. Use doc.importXFAData() to import student data
3. Use loadXML() to re-integrate the original form data from the form variable.

A second approach would be to load the data as an attachment:
1. call doc.importDataObject() to load the XML data
2. Extract the contents of the attachment and append it to your form data with loadXML()
3. Delete the attachment.

good luck.

John

John,
I can do this with Livecycle Designer 8.2? If so, would it be possible to point me in the right direction?

Mo:

Well, turns out it's complicated. So much so that I wrote a blog entry describing how to do it:

http://blogs.adobe.com/formfeed/2009/11/working_with_multiple_datasets.html

good luck!

John

hello,

maybe is not the correct place to put this question, but I really need some help and it seems that I've got stuck somewhere in the process of searching the solution.

I'm using Adobe LiveCycle Designer 8.2 and I've created a form. each control on the page is bind to a node in the data connection (an xsd schema). At some point I export the data into an XML file (using xfa.host.exportData method). This method creates a document containing all the nodes for the fields I have. What I really want is to eliminate the nodes from the XML for which no information was inserted.

after some internet digging and tries, the conclusion that I've come to (maybe wrong) is that in order to not export a field into the XML, the default bind needs to be "none".

I've tried to dynamically change it using the following javascript code:

textField.bind.match = "none";

but with no success. Does anyone knows how can I do this or what I'm missing? All I can find are questions with no answers.

Thank you,
DeeDee

DeeDee:
Binding properties cannot be changed in script.
The reason is that binding happens before scripts get executed.
However, you can set your field to binding="none" in the Designer UI. Select your field and go to the object palette and choose the Binding tab.
Then select "No data binding".

However, from your description, I suspect that binding="none" is not really what you're after. Binding="none" will mean that the field data is *never* output to the xml file. I think you want the field excluded only when it's empty.

This problem is on my list of future blog topics. I'll probably get to it soon, but in the mean time, you could download the xfa 3.0 spec and read about the dd:nullType attribute (page 914).

Good luck!

John

Mr. Brinkman,

Thank you for your answer. At least I know I'm not on the good path and try to find another solution.

Have a nice day,
DeeDee

Leave a comment

About this Entry

This page contains a single entry by John Brinkman published on September 18, 2009 10:27 AM.

XFA Scripting API Reference was the previous entry in this blog.

Editable Floating Fields is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.