Main | June 2006 »

May 30, 2006

Phoenix Was Awesome!

Well, the 37th BFMA Symposium is already over and it seems like it went by way too fast!

I just wanted to thank all the members that came to see us at our Vendor Suite and pulled us asside in the halls to ask questions and give us feedback. Although the sessions I attended were all very interesting and beneficial, the best part was getting to meet you in person and talking about your issues and being challenged to find solutions to your problems (especially with Designer).

Here are some of my memories of Phoenix:

BFMAPhoenixMay2006_1.JPG

Scottsdale was a really nice place to shop!

BFMAPhoenixMay2006_2.JPG

A cactus almost as tall as a palm tree! Back home, a cactus is considered a bonsai since they don’t grow to be more than 2 inches tall.

BFMAPhoenixMay2006_3.JPG

This flower seems to be everywhere on the side of the roads in Phoenix — beats the dead brown bushes and ashphalt we have on ours…

BFMAPhoenixMay2006_4.JPG

The entrance to the Adobe Vendor Suite at the symposium hotel.

BFMAPhoenixMay2006_5.JPG

This was where the symposium took place — a really nice resort with lots of family-oriented activities.

BFMAPhoenixMay2006_6.JPG

Cameron, AZ. Who knew there was a town named after me? ;)

BFMAPhoenixMay2006_7.JPG

The Grand Canyon — pictures don’t do it justice! Absolutely spectacular!

BFMAPhoenixMay2006_8.JPG

Another view of the Grand Canyon.

May 25, 2006

Remove, Remerge

There’s a bug currently logged against Acrobat 7.x where removing an instance of a dynamic subform, using the Instance Manager (IM)’s removeInstance(int) method, doesn’t cause an update to the IM’s count of currently instantiated subforms.

OK, so maybe that was a little too technical so I’ll try to simplify: In Acrobat 7.x, when you remove an instance of a dynamic subform using the IM’s removeInstance(int) method and then check the number of remaining instances using the count property, the number you’ll get won’t reflect the number that remains.

Adobe is aware of this bug and will hopefully be providing a fix for it in an up-coming release.

Fortunately, there’s a simple work-around (and even if the bug gets fixed in a future release, you should probably be checking the version of Acrobat that’s running your form to determine whether you need to be using the work-around or not):

_DynamicSubformInstanceManager.removeInstance(3);

// Force a remerge of the form's current data set with its template
//  to cause an update to the "count" property of all IMs
xfa.form.remerge();

Calling the Form object’s remerge() method will “force the remerging of the data model and template model to re-create the form model”, as explained in the Adobe XML Form Object Model Reference. This means that the form’s current data set will be remerged with the template (the form objects like dynamic subforms, fields, etc.). The result will be a form that looks exactly the same as it did in its state just prior to calling xfa.form.remerge() but with all IMs properly updated to the current number of dynamic subforms which they’re managing, thus correctly updating each IM’s count property.

Even if this problem is addressed in a future release, you’ll want to check the version of Acrobat that’s running your form in order to know whether it has the fix or not (in JavaScript as follows):

// get the host's version as a string and split it into an array
var oVerArray = xfa.host.version.split(".");

if (oVerArray[0] == "7")
  xfa.form.remerge();

Or more simply:

if (xfa.host.version.split(".")[0] == "7")
  xfa.form.remerge();

May 20, 2006

Add, Recalculate

Here’s a little something that very important to know when working with dynamic subforms and using their Instance Managers (IMs) to add instances of them to a form: New subform instances aren’t automatically incorporated into the form’s calculations!

You would immediately notice this problem if you had a form which calculated a total based on line items which can be added or removed from the form. When you add a new line item using the IM’s addInstance(bool) method, the grand total of, say, the cost of all line items isn’t updated to reflect the data you enter into the new line item. This is because the new line item hasn’t been made part of the form’s set of calculation dependencies and is therefore not accounted for by your “grand total” calculation. As a matter of fact, until a call to xfa.form.recalculate() is made, none of the new instances added will be included in the form’s calculations.

To demonstrate this, I’ve created a sample form which has a “line item” section to which line items can be added. As line items are added and their costs changed, the grand total fields at the bottom calculate the total cost of the order: One grand total field performs its calculation using the FormCalc SUM function and a SOM Expression identifying the collection of fields to total while the other uses the LineItem dynamic subform’s IM object to calculate the grand total. Finally, there’s a check box at the top of the repeating “line item” section which, when checked, will cause the “Add Item” button to execute the xfa.form.recalculate(true) statement after adding a new line item. To see the difference, play around with the form by adding line items with the check box both checked and unchecked.

Download Sample [zip]

Minimum Requirements: Designer 7.x, Acrobat 7.x.

BFMA, Phoenix, 2006

I’m very excited to be attending the BFMA’s 37th International Symposium on Forms and Business Processes this coming week in Phoenix, AZ.

Adobe is one of many sponsors for the event. It’s going to be a very exciting time for all of us!

If you have any questions about how to do stuff in Designer as well as some of the things you’d like to see in future releases, don’t be shy! I’m always trying to find new ways to make our product better and I’d love to meet you and get some feedback on what you like and what you don’t like.

May 19, 2006

Is that Field Empty?

As a form designer, you can be almost certain that there will be a time when you’ll need to check a field to see whether its value is empty (whether the form filler has specified a value).

For longest time, I had been doing it like this in JavaScript:

if (theField.rawValue == null || theField.rawValue.length == 0)
  // field is empty
else
  // field has been filled

That served me quite well until yesterday when I found myself needing to do the same, but this time in FormCalc. Theoretically, the following should’ve worked:

if (theField == null | theField == "") then
  // field is empty
else
  // field has been filled
endif

But for some reason, the FormCalc interpreter was giving me a hard time with the null keyword (maybe it’s because I don’t use it very often and it was upset at me, I don’t know — if you’re a developer, you’ll understand that sometimes, code can have feelings and a mind of its own ;) ) so was forced to try and find some other way to check if a field’s value is null in FormCalc and I found one:

theField.isNull

The isNull property (of the XFA Scripting Model’s node object) “indicates whether the current data value is the null value”, as stated on page 422 of the Adobe XML Form Object Model Reference (located in the XML section).

This means that the following JavaScript expression checks whether a field’s value is empty:

(theField.isNull || theField.rawValue.length == 0)

And in FormCalc:

(theField.isNull | theField == "")

May 18, 2006

Instantiating Subforms at Runtime

Something that’s often desired, when working with dynamic forms, is to let the user specify, at run time (in Acrobat), how many lines of information are required to specify what they want.

The classic example is a Purchase Order form on which there are objects (usually buttons) which let you add and remove item lines from the section of the form which calculates the total cost of the order based on how many items are being ordered and the quantity and cost of each item.

In order to achieve this, the use of the Instance Manager is required. The Instance Manager is an object which exists only on repeatable subforms (this could be a subform which is parented to a flowed subform and set to repeat for each data item or it could be a row in a table — since rows and tables are, essentially, subforms). With this special object, you can modify the set of instances of a repeatable (dynamic) subform.

You can access the Instance Manager in two ways. The first is by accessing the object from the dynamic (repeatable) subform:

// append a new instance of DynSubform within its parent container
DynSubform.instanceManager.addInstance(true);

// ensure that the new instance is included in
//  subsequent calculations
xfa.form.recalculate(true);

The second is by using the shortcut “underscore” syntax (note that this syntax is supported if the form is rendered to PDF):

// remove the third instance (zero-based) of DynSubform
_DynSubform.removeInstance(2);

// work-around for Acrobat 7.x bug (reported) where number of
//  instances isn't properly updated after calling removeInstance
xfa.form.remerge();

The drawback to using the first syntax is that there must always be at least one instance of the dynamic subform which exists. If you have a form which must initially not have any instances of a particular dynamic subform, then you need to use the second syntax with the underscore since that special object is always “available” to you regardless of the current number of instances of the dynamic subform in question.

It’s also important to pay special attention to the minimum, maximum and initial number of instances of a dynamic subform. This is set on the Object palette’s Binding tab once you’ve specified that the subform is to Repeat for Each Data Item. The min and max limits must be repected at all times when using the Instance Manager. For example, if you set a min count of 1 and attempt to remove the last instance of a dynamic subform, an exception will be thrown which will result in a script error. You can check the current number of instances by using the Instance Manager’s count property and compare it to, for example,

// get the minimum number of instances that can be instantiated
_DynSubform.min

There are other things you can do with the Instance Manager such as setting the number of instances (using its setInstance(int) method), move instances around (using its moveInstance(int, int) method) etc. These are covered in detail in the Adobe XML Form Object Model Reference (located in the XML section). Check-out the Manipulating instances of a subform section on page 675.

May 15, 2006

Calculate Scripts

I just learned something very interesting while answering a forum thread just now which I thought I should share with the rest of you.

In all the scripts I’ve written so far, I had never realized this simple rule: When writing calculation scripts to set the value of certain fields, the line in your script which assigns a value to the field on which the Calculate event was fired should always be the last line in your script. This is because the last-executed line in your script is the one that the XFA Object Model will use to determine the value of that field.

While it’s generally not recommended to do much more than set a field’s value in the Calculate event, reality is that most of us use it for much more than that: Since it fires on all fields on a form after a field’s value changes, we may use it to affect the presence, color, dimension, etc., of some fields which depend on the values of other fields. Therefore, it’s very easy to fall into this trap where your calculation script isn’t working probably simply because you haven’t structured it correctly.

Take the following FormCalc script as an example:

if (NumericField1 > 0) then
	NumericField1 * 10;
	$.presence = "visible";
else
	0;
	$.presence = "invisible";
endif

This scripts runs in the Calculate event of a numeric field and, if NumericField1’s value is greater than zero (0), results in the field becoming visible and its value set to the product of NumericField1’s value multiplied by 10. If NumericField1’s value is zero (0) or less, its value is set to zero (0) and it becomes invisible. Note the order of the lines which set the field’s value and the ones which set its presence: The value is set before the presence is set.

The result of this script is that the presence is affected properly depending on the value of NumericField1 however, since the last-executed line is “$.presence = ‘visible’” (or “invisible”), the value acutally assigned to the field is “visible” (or “invisible”) which evaluates to “0” as a numerical value. Therefore, with this script, the value of the product field would always be zero (0).

If you change the order of the lines which set the field’s value and presence as follows:

if (NumericField1 > 0) then
	$.presence = "visible";
	NumericField1 * 10;
else
	$.presence = "invisible";
	0;
endif

Then both the field’s presence and value will be set correctly because the last-executed lines in each block are the ones which set the field’s calculated value.

May 10, 2006

Data-Nominated Subforms

I thought I would post a little sample on data-nominated subforms tonight. This is a new feature, introduced in Designer 7.1, which can be a very powerful tool.

Since the feature essentially lets you define expressions against values from data being loaded into a form in order to control which subform, from a specified set (subform set containing subforms), will be used for the record currently being loaded into the form, you can do very interesting things. For example, you might have a table which lists data. Maybe you would like to use a row which has a yellow background to identify data which is not important, one with an orange background for data that’s important and one with a red background for data that’s very important. This can be easily achieved using Data-Nominated Subforms.

I’ve created a little sample based on the movie data I used for the Conditional Breaks Sample I posted a few days ago. In this sample, I have a subform set which contains 3 subforms, each capable of binding to a movie record. The twist is that I want to use the green subform for comedies, the red one for action movies and the blue one for dramas.

Download Sample [zip]

Minimum Requirements: Designer 7.1, Acrobat Pro 7.0.5.

Note: A basic understanding of Data Binding is required for this sample.

The trick in getting this to work is to first create a data connection which represents the data you’re loading into the form.

Then you need to create the subforms which each contain fields with names set to the data node names from a movie record so that Normal (“implicit”) binding works correctly once the subform is picked for the record.

The next step is to wrap all subforms into a subform set. You can do this by selecting them all in the Hierarchy palette and then choosing Wrap in Subform Set from the context menu.

At this point you’re almost done. All that’s missing are the expressions which tell the Object Model which subform from within the subform set to use for a particular data record.

The Data-Nominated Subform feature is accessed by selecting the subform set and going to the Object palette’s Subform Set tab. There, you’ll find a Type property from which you can select the Select One Subform From Alternatives item. When you select this option, an Edit Alternatives button will appear. Click on that to open the Data-Nominated Subform dialog. In there, you would elect to Choose Subform Using Expression, select your data connection from the drop down list and then specify an expression like

category == "Comedy"

for the “$record.movie” binding (which establishes the scope for your expression) on the comedy subform.

When you preview the form (if you just run it in Acrobat, you’ll have to import the data into the form using the commands under the File menu), you’ll see that the comedy subform is used only for records which have a “Comedy” category name.

May 06, 2006

Conditional Breaks

Some of you have requested a sample of the new Conditional Breaks feature in Designer 7.1.

This feature allows you to set conditions, based on imported data, which determine when breaks should occur.

For example, you may have data that you want to list by category and every time the category changes, you’d like to start the new category on a new page. Or, you may have a separator that you want to insert in between sets of data on your form.

I’ve created a little sample which demonstrates the Conditional Breaks feature by listing some movie data where each time the category changes, a new page starts. Don’t forget to import the data.xml file into the form when you open it in Acrobat.

Download Sample [zip]

Minimum Requirements: Designer 7.1, Acrobat 7.0.5.

Note: A basic understanding of Data Binding is required for this sample.

Since breaks are only really useful inside flowed subforms (there’s no point setting breaks on a subform inside a positioned container since the data won’t flow — it’ll all stay in one place instead of flowing left-to-right, top-to-bottom, for example), you can only get to the Conditional Break settings on the Object palette’s Pagination tab if you select a subform inside a flowed container (subform). The only exception is selecting a row or a section within a Table object since the content of Tables is always flowed.

Typically, after creating a new form, you would add a positioned subform (let’s call it the ContentSubform) to the page and add the fields you need into it. You would then make the page subform flowed (set the Content property on the Object palette’s Subform tab to Flowed), then select the ContentSubform and click on the Edit button on the Object palette’s Pagination tab. This will open the Conditional Breaks Dialog where you can add multiple conditions for that subform’s breaks.

In my sample, the data represents movies and each movie record has the following information:

  • Category
  • Title
  • Actor

Since I wanted to create a report-style form where a change in category breaks to a new page and a change in the actor’s name is preceeded by a separator, I created a movie subform which lists the data and then two other subforms:

  1. NewCategorySF which I want to insert every time the category changes.
  2. NewActorSF which I want to insert every time the actor’s name changes.

I then specified two conditional breaks on the movie subform:

  1. The first one checks for a change in the category data. When a change occurs, the layout will break to the top of a new instance of the “page 1” subform and, before starting to list the data for the new category, insert an instance of the NewCategorySF subform. This is what we call a Break Before Conditional Break (since the break occurs before the new record, which contains the new category name, is inserted into the form). Notice that the NewCategorySF subform is listed as a Leader for the Conditional Break because it’s meant to be inserted after the break but before the new record gets inserted into the form.
  2. The second checks for a change in the actor data. When a change occurs, the layout will not actually break because we simply want to use the Conditional Break to know when to insert the NewActorSF to separate the data set into sub-groups of actors. In this case, I specified NewActorSF to be a Leader for the Conditional Break because I wanted it to show-up after the actual break but before the new record is inserted into the form. You may wonder why I didn’t make it a Trailer but in this case, there wouldn’t be any difference since there isn’t an actual “physical” break in the data flow (to a new page or content area, for example).

Also notice that the NewCategorySF and NewActorSF subforms are both set to Repeat for Each Data Item (you can set this on the Object palette’s Binding tab) with a Min Count of 1 so that they always appear before the very first data record is inserted into the form. Otherwise, they wouldn’t appear until the first Conditional Break occurs.

Samples! Give Me Samples!

One of the best places to look for Designer samples is on the LiveCycle Designer Samples Site. There, you’ll find lots of samples demonstrating various Designer techniques and features.

Although new samples are constantly be posted to the Designer Samples Site, they can’t always cover all topics or address all of our customer’s questions/issues. As such, I’ll be posting some of my own samples to my blog. Look for them under the Samples category.

May 05, 2006

What's new in 7.1?

For those of you still running Designer 7.0 or earlier, check-out the new stuff available in 7.1 on the Adobe LiveCycle Designer site.

There’s a “what’s new” link under the Learn More column.

Among my favourites are:

  • New Table object — play with rows and cells to get really nice layouts.
  • Dynamic Properties — automatically populate list objects with data from data connections.
  • Paper Forms Barcodes — reduce data entry errors by encoding field values into a barcode which can be scanned from paper once the form has been filled, printed and manually signed.
  • Conditional Breaks — use changes in your data to trigger breaks in your pagination (e.g. when the category changes, start the new one on a new page).
  • Data-Nominated Subforms — use changes in your data to determine which subform should be filled next.

Debugging Scripts

Here’s a simple tip that could make a huge difference in your ability to debug your scripts in Designer:

If you use the JavaScript language for a script, you can use the following function to output information to the Acrobat Console:

console.println("string");

Anyone who has attempted to debug their script(s) in Acrobat knows that it’s a painful thing to do. Unfortunately, many only know about

app.alert("string");

or

xfa.host.messageBox("string");

which gets the job done but not without some headaches and, in certain cases, RSI in your index finger! The other problem is that showing a message box can cause differences in the form’s behaviour especially if you’re trying to debug a script which is setting focus to an object on your form. The fact that a dialog is displayed can really mess things up.

By using console.println, you can output text to the Acrobat Console (when in Acrobat — even Preview in Designer — just press Ctrl + J to display it) so that you don’t change the behaviour of your scripts.

The ability to debug scripts is something we know needs serious attention in Designer and trust me, we’ve talked about it and we have plans to address these issues but I can’t speak about anything definite at this time.


Updated: November 1, 2006

Who Am I?

You may recognize FormBuilder as an alias for posting on the Adobe LiveCycle Designer 7.x forum . That would be me!

My name is Stefan Cameron. I’m a software developer at Adobe and I work on our LiveCycle Designer product.

This blog will be about designing forms using Designer. Unless specified, my posts will refer to the latest publicly-released versions of Designer and Acrobat. At the time of this post, that’s Designer 7.1 along with Acrobat 7.0.7.