Working with Multiple Data Records

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.

11 Responses to Working with Multiple Data Records

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

  2. Jake says:

    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

  3. 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.htmlHopefully there’s enough there so you can code by example.John

  4. mo says:

    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

  5. 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 variable2. Use doc.importXFAData() to import student data3. 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 data2. Extract the contents of the attachment and append it to your form data with loadXML()3. Delete the attachment.good luck.John

  6. Mo says:

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

  7. 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.htmlgood luck!John

  8. DeeDee says:

    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

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

  10. DeeDee says:

    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

  11. scott says:

    Hi John,

    I just got through reading data from a database using the SourceSet object and it works well, but as I was reading how to read/write to a database, I came across a statement:

    Database.ExecSQL() where you can pass a SQL statement. It would be nice if you had an article on this command since it seems hard to find any documentation on it. or how this relates the the data Source object.

    thanks,
    Scott