Archive for October, 2011

Tables with Variable Numbers of Columns

Support for tables in Designer assumes you know in advance how many columns there will be in your table. Occasionally this will not be the case.  You might want a table that adapts to the number of columns that occur in your data.  In other words, you want a table with rows where a cell is represented by a repeating subform.  And while Designer doesn’t support creating such a table via the table UI, it is possible to create one using a bit of script

The technique involves leverages the fact that tables and rows are really just subforms with special attributes.  So when designing the form, we create:

  • A subform to represent the table.  Make it a subform where content flows top-to-bottom.
  • Inside the table subform create one or more row subforms.  Make the row subforms flow left-to-right.
  • Inside each row subform, create combinations of fields, text and subforms that will be treated as columns.
  • It’s best to make the cells growable in width and allow the layout engine to determine best fit.  If you want control over width, leave the columns growable and set the columnWidths attribute on the table subform (see script below)

And then to turn this into a table, add this initialization script to the table subform:

// mark this subform as a table
this.layout = "table";

/** 
* if needed, we could explicitly set the column widths * If left alone, the columns will be auto-sized */ //this.columnWidths = "1in 2in 2in 2in 1in";

And then for each row in the table, add this initialization script:

// mark this subform as a row
this.layout = "row";
if (this.instanceIndex === 0) {
    // mark the first row as a header
    this.assist.role="TH";
} else {
    // mark each subsequent row as a regular table row
    this.assist.role="TR";
}

Note that setting the role attribute is very important for accessibility — it allows the screen reader to use the table header cells to describe the contents of the table body cells. Here is a sample form that implements this technique — along with sample data for previewing.

Fix Layout Problems

This is the fourth of four exercises that were part of the LiveCycle best practices lab at MAX.

Fix Layout Problems using XFADebugger

The purpose of this exercise is to fix layout problems by analyzing their PDF with XFADebugger.pdf.

In Designer, open: EX4 LayoutProblem.pdf (with preview data file: afiList.xml)
In preview, notice that :

  • Content was pushed off the first page
  • The second page is truncated

Open XFADebugger.pdf in Acrobat. Use the “Load XFA/PDF file” to open EX4 LayoutProblem.pdf.

  • Explore the content in the debugger view. Expand the trees. Click on nodes.
  • Click the “Find Warnings” button several times to see various warnings reported.
  • Fix the warnings in Designer

Here is the file with the layout problems fixed: EX4 LayoutProblem (Solution).pdf

Moving code into a shared script object

This is the thrid of four exercises that were part of the LiveCycle best practices lab at MAX.

Create a shared script object fragment

Open the file: EX3 addressCopy.pdf in Designer

Preview the file with sample data and note the behavior when you check "same as billing".  The shipping address values are greyed out and the values are synchronized with the billing address.

The Exercise:

  • The logic to copy values and to change the appearance of the destination subform is implemented under the
    “Same as Billing” checkbox.
  • Take the script methods to copy subforms and “grey-out” data, centralize them in a script object
  • Create a re-usable fragment from the script object

Bonus 1:

  • Change the form properties so that script changes are not preserved:
    Form Properties/Run-time/Preserve scripting change… : Manually
  • Open the form in Acrobat, select “Same as Billing”, save, close and re-open the form
  • Notice the Shipping address is no longer read-only
  • Fix by applying the read-only setting in an initialization script

Bonus 2:

  • Both instances of the country field populate a list of provinces or states. Centralize/share this code.

Solution: EX3 addressCopy (Solution).pdf

Customize a Validation Experience

This is the second of four exercises that were part of the LiveCycle best practices lab at MAX

Implement Field Validation

Open EX2 Validation.pdf in Designer

  • Preview and click the “Submit by email” button to validate the form
  • Notice:
    • Errors are reported with message boxes. 
    • Selecting the error message in the listbox highlights the error field
    • Tabbing out of the listbox will set focus to the selected error field.
  • Change the form properties to customize error handling to: “Don’t show any message boxes”
  • Modify the (propagated) validationState event under the root subform:
    • On error: color fields and modify the toolTip value
    • Else: revert field color and toolTip
  • Bonus:
    • Add a change event to the phone number to restrict entry to digits

The solution file is: EX2 Validation (Solution).pdf

Reverting changes to fields:

To write re-usable code for changing values back to their original state, we should never hard-code the changes. E.g. we should never revert a color by writing: Field.fillColor = "255,255,255". The original color may not have been white.

Rather, after modifying an element, we can revert by removing the changed element. Removing an element forces the XFA processor to use the original value found in the form template.

E.g. Change the font color by modifying the font.fill.color element:
Field.font.fill.color.value = "255,0,0";

Revert the change by removing the color element:
Field.font.fill.nodes.remove(Field.font.fill.color);

Or by removing one of the ancestor properties:
Field.nodes.remove(Field.font);

Changing/filtering keystrokes

  • In the change event, xfa.event.change holds the contents of your key stroke (or paste buffer)
  • E.g. force all input to upper case:

    xfa.event.change = xfa.event.change.toUpperCase();

Script Performance Exercise

I enjoyed attending MAX last week. The best part was putting some faces to names of people who had previously been cyber-personalities.

We did a pre-conference lab on LiveCycle best practices. For the lab I prepared four exercises on various aspects of form design.  For those who weren’t able to attend, I’ll post those exercises as a series of blog entries.

Exercise #1: Measure and Improve JavaScript Performance

In this exercise we optimized a fairly simple script – a few lines in a loop. Depending on your machine, you should be able to improve the performance anywhere from a factor of three up to a factor of seven.

Open EX1 Performance Tests.pdf in Designer.

There are 50 subtotal values that need to be totaled. Each total has a test button that executes the Total script 500 times and measures the performance.

Note the 4 variations of the sum function:

  • Test 1: The original (slow) version of the calculation
  • Test 2: A copy of Test 1 that you will improve. 
    Look at the hints in the code comments in order to improve the script.
  • Test 3: The solution
  • Test4: Same calculation expressed in FormCalc

Understanding the solution:

  • Undeclared variables are very expensive to reference
  • Javascript variables are far less expensive to reference than XFA objects
  • Evaluate as few dots as possible. e.g. c.d is faster than a.b.c.d
  • Dots evaluated inside a resolveNode(s) expression are faster than dots evaluated in JavaScript objects. E.g. resolveNode("a.b.c") is faster than a.b.c

Why is FormCalc faster?

All references to XFA objects from JavaScript involve a round-trip from the script environment to the XFA processor and back. These round trips are expensive. 

FormCalc runs native in the XFA processor and there are no round-tripping costs.