Stefan Cameron on Forms
Building intelligent forms using Adobe LiveCycle Designer

Instance Manager Object Reference

It seems lately a lot of my posts and a lot of the comments that you’ve posted had something to do with the Instance Manager: The object available only on repeatable (dynamic) subforms which allows you to manage that subform’s instances (add, remove, etc.).

Since it’s at the core of repeatable subforms which are necessary when designing flowable (dynamic) forms that, say, display one row per record in a table connected to a database, I thought it would be useful to give a quick overview of the Instance Manager Object’s properties and methods.

Accessing the Instance Manager

Before we get to the properties and methods, here’s a refresher on how to get at the Instance Manager for a particular subform (or table row).

Repeatable Subform Required

First, you must make the subform repeatable. To make it repeatable, it must be placed in a flowed container — that is, another subform whose Content type (found on the Object palette’s Subform tab) is set to flowed.

Side Note: The simplest case is a report-style form that simply displays records from a database where each record’s information is displayed in separate fields within a subform. So you have a subform with fields in it that are bound to data nodes in some data connection and the subform itself is bound to a repeating data section in that same data connection. For this scenario, you’ll find it much easier to place your fields on the first page and then shrink the page (which is a subform) such that it’s snug against the fields you placed on it and looks more like a row rather than a page. This is because the page subforms are, by definition, subforms parented to the root subform (named "form1" by default on new forms — check it out at the top of the Hierarchy palette) and the root subform is, by definition, flowed. By using the page subform as your repeatable subform for your data records, you’ll find it much easier to quickly get to a state where all records show-up on your form and new pages get added when previous ones are full (can’t fit any more rows).

Once the subform is placed in a flowed container (parent subform), you must then specify that it’s a repeatable subform by going to the Binding tab on the Object palette and checking the "Repeat subform for each data item" box.

After these two easy steps are complete, you’ll then automagically get an Instance Manager object on the repeatable subform you just defined.

Script Access

It’s nice to have a repeatable subform but unless you’re just using the default behaviour (which may very well be just fine in most cases), you’ll need to write scripts that use it’s Instance Manager’s properties and methods and you’ll need to know how to access the Instance Manager in your scripts.

It turns out there are two ways of doing this:

  1. instanceManager Property: As long as you have at least one existing instance of the repeatable subform, you can access its instanceManager object directly like you would any other property: RepeatableSubform.instanceManager. Note, however, that this property will only be accessible if at least one instance of the RepeatableSubform object exists (which could be a problem if you’ve specified that its minimum allowed number of instances is zero and its initial instance count is zero as well).
  2. Underscore Prefix: The other recommended way to access a repeatable subform’s Instance Manager is to use, for lack of a better term, "the underscore-prefixed repeatable subform name" object. That is, whenever a subform becomes repeatable, its Instance Manager is added to the Script Object Model as a child of the subform’s parent container (remember, that’s the flowed subform from earlier) and is given a name that is the repeatable subform’s name with a "_" prefix. Therefore, if your subform was named "RepeatableSubform", its Instance Manager would get a name of "_RepeatableSubform" and you would access it as a property of the flowed container subform like this: FlowedContainerSubform._RepeatableSubform. The nice thing about accessing it this way is that you always have access to it — even if no instances of the RepeatableSubform object currently exist.

Properties and Methods

Now that you know how to make a subform repeatable and get access to its Instance Manager object when writing scripts, here are the various properties and methods that you have access to:

Properties

  • count: Returns the number of instances that currently exist. This is very useful when writing loops that do something to each instance.
  • min: Returns the minimum allowed number of instances. When removing instances, you’ll get a scripting error if the resulting number of instances is less than the minimum and the minimum number of instances will still remain (even if you meant to remove them). This can be set using the Min property on the Binding tab of the Object palette or my accessing the subform’s "occur" element and setting its min property: RepeatableSubform.occur.min = "value".
  • max: Returns the maximum allowed number of instances. When adding instances, you’ll get a scripting error if the resulting number of instances is more than the maximum and no additional instances will be added. You can modify this in the Object palette’s Binding tab or with the following property: RepeatableSubform.occur.max = "value".
  • occur: This is a reference to the pertaining subform’s <occur> element which lets you modify its minimum (min), maximum (max) and initial (initial) number of instances.
  • name: This property sets the name of the Instance Manager object itself. I doubt this would ever really be useful but it’s a property nonetheless. Be forewarned, however, that it’ll affect all subsequent scripts. For example, if you were to write "FlowedContainer._RepeatableSubform.name = ‘myIM’;" and then write "FlowedContainer._RepeatableSubform.addInstance(0);", you would get a an error stating that "_RepeatableSubform" doesn’t exist. That’s because you’ve changed its name to "myIM" and therefore should write "FlowedContainer.myIM.addInstance(0);".

Methods

  • addInstance(bool merge): Adds a new instance of the repeatable subform and returns a reference to the new instance (or null if no instance was added) . Setting merge to 1 will cause any additional data to be merged with the new instance. Also, don’t forget that new instances aren’t automatically added to the form’s calculation dependencies. Also, be careful not to add more than the maximum number of allowed instances (see the max property).
  • removeInstance(int index): Removes the instance with the specified zero-based index. [A/R 7.x] You may need to call xfa.form.remerge() after removing an instance. Also, be careful not to remove more than the minimum number of allowed instance (see the min property). [A/R 8.1+] If there’s anything else you need to do (any other script statements) along with removing an instance, make sure that the instance is removed once all other statements have been executed. The new Direct Rendering engine in A/R 8.1 removes the instance, and all objects that it contains, immediately (as opposed to removing it on the next rendering pass as in previous versions) which means that any subsequent statements are not executed.
  • moveInstance(int fromIndex, int toIndex): Moves the instance at the zero-based fromIndex such that it becomes the instance at the zero-based toIndex (and other instance in between are shifted up/down as necessary).
  • insertInstance(int position [, bool merge = 0]): [New in A/R 8.0] Inserts a new instance at the zero-based position specified and returns a reference to the new instance (or null if no instance was added). You may optionally specify 0 or 1 for the merge property which, if set to 1, will merge any additional data with the new instance. The same rules and gotchas apply for this method as they do for the addInstance method described above.
  • setInstances(int count): Adds or removes instances to/from the end of the set depending on the difference between the current instance count and the specified instance count. In other words, if there are currently 4 instances and you call "setInstances(6)", two new instances will be added to the end of the set. Conversely, if 3 instances exist and you call "setInstances(2)" the last instance in the set will be removed. Note that this method abides by the currently-specified min and max restrictions.

Recommended Scripting Language

On a last note, I simply wanted to recommend using JavaScript as opposed to FormCalc when scripting the Instance Manager — especially when adding or removing instances. While FormCalc technically fully supports the Instance Manager, I personally find it’s a little flakey. JavaScript support, however, is quite consistent and stable.

Updated: June 26, 2009


Posted by Stefan Cameron on November 11th, 2006
Filed under Instance Manager,Scripting
Both comments and pings are currently closed.

117 Responses to “Instance Manager Object Reference”

  1. chris surtees on November 28th, 2006

    I have a form where users can enter a case number and click a button. The button then creates 3 subforms throughout the main form using addInstance(). In the first subform there is a button that says “Delete Section”. Once that is clicked I want all 3 subforms to be deleted. The user may have created a number of sections so I need to identify the correlating two sections.

    I thought I could just rename each subform based on the case number that the user entered and then reference each subform to be removed that way, but it doesn’t work.

    I can ‘apparently’ rename the subform, but when using the new name when using removeInstance() it doesn’t work.

    Test rename of subform:
    // this is in the initialization parameter of the field containing the policy number

    this.rawValue = “123”
    app.alert(this.parent.name); // provides the current subform name correctly.
    this.parent.name = this.rawValue; // should change the subform name to “123”
    app.alert(this.parent.name); // shows the value “123” which is what I would expect and want

    BUT

    if I now click a button saying: ._123.removeInstance(0);
    it doesn’t work.
    If I now click a button saying: .OriginalFormName.removeInstance(0);
    it works.

    So it doesn’t appear that the subform name is really being changed at all. It’s a ruse 🙁

    Any help would be great.

  2. Stefan Cameron on November 30th, 2006

    Chris,

    When working with the Instance Manager (IM), it’s important to note that this object controls the instances of its pertaining repeatable subform which currently exist at runtime.

    When you add instances, you use the IM’s addInstance method and you must do the same when removing instances.

    For example, if you had a simple form with a repeating subform and two buttons: an “add” and a “delete” button. These buttons are outside the repeatable subform named “RepSF” (they’re siblings to it). The “add” button’s Click event would have JavaScript like this:

    _RepSF.addInstance(0);

    and the “delete” button’s Click event would have script like this:

    _RepSF.removeInstance(0);

    Based on your question, I think you’re probably following me at this point. I think where you’re going wrong is you’re expecting to call “removeInstance(0);” on the actual instance of a repeatable subform, which you’ve renamed, in order to remove it.

    Your attempt is definitely a logical one but it’s not quite how things work with the IM and since you need to make sure you only remove the instances that pertain to a specific policy number, you’ll need to use a different approach.

    I would recommend you use a hidden field inside the repeatable subform. Let’s call it “PolicyNumber”. When you add a new instance of that subform, you would do this:

    var oNew = _RepSF.addInstance(0);
    oNew.PolicyNumber.rawValue = “123”;

    Remember that the “addInstance” method returns a reference to the new instance so you can access the fields it contains just like you would a normal subform using that reference.

    Later, when the user clicks on the delete button, you would do something like this:

    for (var i = _RepSF.count; i >= 0; i–)
    {
      var oInstance = this.parent.resolveNode(“RepSF[” + i + “]”);

      if (oInstance.PolicyNumber.rawValue == “123”)
        _RepSF.removeInstance(i);
    }

    Note that the loop reverses through the instances because we’re removing some. Removing instances while looping forward through the set will get you in trouble because the “count” property will change on you and you’ll end-up walking over the edge of the array pretty fast.

  3. smvo on January 15th, 2007

    Hi Stefan,

    There are some questions I would like to make relating to Instance Manager:

    1 – I know one can define at design-time a initial count to a repeated section, so that when the form open, the user will see that number of instances. But what about see those instance at design-time, also?

    2 – I´m truing to programmatically set values to fields in a repeating section using InstanceManager.If there is a previous instance (row) at design-time, because I´m adding additional instance my data was not being place at the first row. However I change my script and now it works.

    Then I set the initial count property to 2 and I realize the second row would never be filled with data, as if I write the first 2 rows values at the same instance. To set the values to these predefines instances I´m using:

    fldRegNodes.item(i).parent.ItemSet.nodes.item(auxinst) -> I think “item” will give me a reference to the “n” item in the field set, right?

    Thank you!

  4. Stefan Cameron on January 17th, 2007

    smvo,

    1. I agree, being able to see the initial number of instances as defined by the min and/or initial properties at design-time would be very nice. Unfortunately, that’s not something that Designer is capable of doing at this time.
    2. In order to properly reference instances of subforms (whether they’re “plain” repeating subforms or they’re repeating rows in a table), you shouldn’t use the “nodes.item” method (since it doesn’t only iterate through child nodes but also child elements which are properties of the node itself).

      To do this properly, you should use the resolveNode method on the container of the repeating subform like this:

      var oItem = ItemSet.resolveNode(“RepeatingItem[” + i + “]”);

      The above JavaScript code will return a reference to the ith instance of the “RepeatingItem” subform inside the “ItemSet” container (e.g. replace “i” with “2” and you’ll get the 3rd instance since they’re zero-based).

  5. Raül Romero on January 18th, 2007

    Hi Stefan.

    I have a problem related to the InstanceManager.
    I have a subform defined as a repeater. The data inside the repeater is bound to an XML schema.
    As a sibling of the repeater, there is a button to add a new instance. When it is clicked, I call the script “global.InvitedTowns._InvitedTownSubform.addInstance(1);”.
    The new instance is properly created but some of the data in the repeater is lost when I reopen the form.

    I could notice the following:

    1. The data lost is that of the complex subtype in the XML schema. That is, the repeater subform is bound to the element ” invitedTown[*]” of the XML schema. The simple elements inside there are properly kept; i.e. field “name”. But the field with an extra level of depth in the XML schema (those regarding the address of the town) are lost: “address.street”, “address.postalCode” and so on.
    2. The data of the first instance (default instance already existing when opening the form) is properly stored, also for the “address” details. However, the address data for the new instances created dynamically is lost after saving, closing and reopening the form.
    3. As a workaround, if I open an empty form, add all the needed instances (empty), save it and close it, I can properly fill in the data after reopening the form and nothing is lost afterwards.

    After all those tests, I guess there could be a bug related the dynamically generation of instances (maybe, memory management) and the binding of those dynamic instances with complex XML structures.

    Have you detected any similar problem or do you have any suggestion to solve it?

    Many thanks.

  6. Stefan Cameron on January 19th, 2007

    Raul,

    This sounds like a data binding issue with the “address” schema node to me.

    I tried defining a little schema that had a repeating section named “invitedTown” with a nested complex section named “address” containing some more children. I was successful in creating a form with an “add” button that would let me add instances of the repeating subform representing the “invitedTown” node, enter some information, save the data, close the form, re-open the form and succesfully import the data I had previously saved. This would restore all data, including the data from the inner address section.

    I would suggest dragging and dropping the “invitedTown” node from the Data View palette onto a form, looking at the way the bindings are setup and trying to replicate that in your form in case one of your data bindings for an “address.?” node isn’t properly setup.

  7. Snehal Shah on January 24th, 2007

    Hello Stefan,
    I am encountering some issues with either .instanceManager or _SubForm.addInstance.
    Basically, I have a form (FRM1 – Flow Content). I have sub form (FRM2 – Position Content, Repeat Subform for each data item, Min 1) in FRM1. Similar to the example you had. I also had one button to add instances of FRM2 dynamically and Email button to see XML. Only code I have is _FRM2.addInstance(1) on click event. When I do PDF Preview, and click on button, nothing happens. The form is not repeated. But when I check the saved XML, I can see two instances.
    I tried the sample from Adobe site http://blogs.adobe.com/formbuilder/samples/im/AddRecalculate.zip, that works but if I try to replicate this same example, no luck.

    Am I missing something here?

    Regards,
    Snehal

  8. Stefan Cameron on January 27th, 2007

    Snehal,

    It sounds like you might either be previewing or have saved your form as a static PDF form. In that case, you won’t see new instances appear but they’ll be added to the data which explains the discrepancy between what you’re seeing on the form and what you’re getting in the submitted XML data file.

    I would recommend you check-out my article on previewing as dynamic PDF. It has tips on previewing and saving your form as a dynamic PDF form, which is what you’ll need to do in order to see the instances appear on your form in Acrobat.

  9. Snehal Shah on January 29th, 2007

    Thanks Stefan,
    I did look at the properties and I made it work. With that working, I have two more question since I tried to implement a hypothetical example.
    In my example, I have to create instances of a subform on the fly, but place them randomly. So e.g. if I have PDF where I have following hierarchy.
    Page 1
    — General Info
    — Client1
    – Personal Info
    – Location Info
    – Order Info
    — IMAGE FILLER SECTION

    Total Info

    In my General Info section, I have an option to say, “How many clients?” and “Per Client, How may products in the order?”.

    So Question 1 is,
    Using the dynamic instancing of subform for client section, can I create a form but place it say after IMAGE FILLER SECTION? I can create one after the other, but I want to have some other form or section in between two instances…

    And Question 2 is, after putting each Order Info page, there is a final total page which I want to show it at the bottom. Which ever page it is on, it has to be at the bottom. Using “Flow Content” layout, it always sticks to the bottom of last object. But I want it to have it at the very end.

    I tried quite a few tricks of using container area placement, overflow.. But got nowhere..

    Any help is sincerely appreciated. I am not sure, whether I shall post this question on Adobe forums or it looks at here.

    regards,
    Snehal Shah

  10. Stefan Cameron on February 2nd, 2007

    Snehal,

    For your first question, I think Designer’s Conditional Breaks feature might be able to solve this problem.

    You can use it to specify that an instance of the “IMAGE FILLER SECTION” should be placed after each instance of a “Client” section without necessarily causing a break to a new page. Simply define a conditional break set to “after” (the Break property), “No Break, Just Leader & Trailer” (the To property) and with its Trailer property set to the “IMAGE FILLER SECTION” subform.

    As for your second question, if you always want to show the “Total Info” section at the bottom of a new page, then all you need to do is place it at the bottom of its own page. Inserting instances of the other subforms in between will simply cause the page to automatically be pushed to the end. If, however, you want the section to always be placed at the bottom of an existing page, whatever page it may end-up on, that’s a different story which I don’t have an answer for.

  11. Michelle Yaras on February 8th, 2007

    Stefan —

    Hopefully a quick question. I have a form where I use InstanceManager to repeat a subform (min. count 2). I would like to repeat a full page of the subform rather than just one subform at a time. I have not max limit set. Is there a way to do this? This form is used to submit a series of data about specific porducts so it is more than one line repeating.

    Thanks

  12. Stefan Cameron on February 9th, 2007

    Snehal,

    A few days ago, I said I didn’t have an answer to your question about placing a particular subform at the bottom of the last page in a form.

    I did a little investigating and it turns out that there’s a way to achieve this but it’s not perfect. New features being released in the next version of Designer will make this possible in the “correct” way but for now, we have to deal with what we’ve got in Designer 8.0 or earlier.

    In order to demonstrate this and give you something to work with, I’ve posted a form which I believe resembles the type of form you’re trying to design.

    First, run it in Acrobat (7.0.5 or later), enter 2 for the client count and notice where the blue “total” subform ends-up: At the bottom of the first (and only page). That’s not difficult to do. Now, enter 3 as the client count and notice what happens: The first page isn’t quite long enough to fit the last “image filler section” so it overflows onto the next page and the “total” subform ends-up at the bottom of the second (last) page.

    If you check it out in Designer (7.1 or later), you’ll be able to see how it works:

    • The Master Page named “Page1” contains two content areas which define where content (the subforms) will flow when the number of instances of the “client info” subform is modified.
    • The root subform (named “form1” at the top of the Hierarchy palette) has a conditional break and an node set on it which specifies that its content should flow into the first content area named “ClientInfoCA”. Unfortunately, I had to specify these manually in the XML Source view since Designer doesn’t let you edit properties of the root subform in its palettes.
    • The “ClientInfoSF” subform also has a conditional break to specify that the “ImageFillerSF” subform should be placed immediately after every instance of the ClientInfoSF subform. This ensures that the image filler section always appears after every client info section.
    • The “TotalSF” subform has a conditional break which specifies that it should break to the “TotalCA” content area before it’s rendered on the form (which means it’ll always show-up in that content area which is located at the bottom of the page).
    • Because the “TotalSF” subform is last in document order (or at the bottom of the hierarchy tree), Acrobat will only attempt to render it once it has finished rendering all prior instances of the “GeneralInfoSF”, “ClientInfoSF” and “ImageFillerSF” subforms, which means that it’ll only show-up once and it’ll always be at the bottom of the last page.

    The only draw-back to this design is that the two content areas can’t overlap or else the subforms may overlap in the rendered form. This means that you get an empty section at the bottom of every page before the last one where the “TotalCA” content area is located.

    If the “TotalCA” content area in your form is small enough, then this might be a viable option for you (the amount of blank space at the bottom of all pages prior to the last one might be negligible).

    I realize it’s a very complicated concept to explain in a comment on a blog so please don’t hesitate to ask more questions if there’s something you don’t understand.

  13. Stefan Cameron on February 12th, 2007

    Michelle,

    I’m not certain I follow you when you say that you

    “would like to repeat a full page of the subform rather than just one subform at a time.”

    Are you saying that you would like to be able to determine the exact number of instances of this repeatable subform that fit on a page and show this number of instances initially, all in script when the form is loaded in Acrobat?

    Or are you simply asking how to specify an initial number of instances of this repeatable subform?

    Have you though about offering a field in which the person may specify the number of products for which they wish to provide data and then simply setting the number of instances to the specified count? This might get around the problem where you would provide too little or too many initial instances of the repeatable subform.

  14. krish on March 13th, 2007

    Hi Stefan,
    I have a form where users can add subforms by clicking a “add_subform” button. when the subforms are added they are given subform numbers in increasing order.
    Each subform has a delete button which when pressed should delete the subform and re-order the subform numbers.
    how can i delete the subforms at runtime.
    what parameter is to be passed to the removeInstance(int) at runtime.

  15. Stefan Cameron on March 16th, 2007

    Krish,

    The “removeInstance(int)” method is expecting the zero-based index of the instance of the subform which you wish to remove.

    If 4 subforms have been added and the user presses the “delete” button on the second subform, you need to call

    _ContainerSF.removeInstance(1);

    where “_ContainerSF” is the instance manager for the flowed subform which contains the instance of the subform that needs to be removed.

    You can get this index number by retrieve the index of the delete button’s parent object (which is the instance of the subform to be removed). From the delete button’s Click event, use this script:

    _ContainerSF.removeInstance(this.parent.index);

  16. MK on May 10th, 2007

    Stefan,

    Scripting and instance manager are totally new to me, so any assistance would be greatly appreciated.

    Question on referencing instances. I have a 2 page form. The first page is a summary table and the second page provides details. So, I have it that I can add (and remove) instances on the summary table. This correspondingly adds pages to the document. Ideally, the way this should work is that the user adds a row in the summary, enters some summary information in the instance that is created and when he/she goes to the corresponding details page, the summary information has auto populated on that page as well (making it easy to find). For example, the first row of the summary table has an Item labelled #1, and in the detail pages, there is a page with a field that has #1 in it as well. So, there is a 1 to 1 relationship between the summary row and a details page.

    Now the question is, how do I push the data in the fields from the summary table instance to the fields in the corresponding details page instance? I can’t figure out how to find and reference the fields in each instance of the subforms so that I can get them to equal each other.

    Thanks in advance for any insights to can provide!

  17. Stefan Cameron on May 12th, 2007

    MK,

    The important detail to note about the addInstance method of the Instance Manager is that it returns a reference to the new instance. That means that when you add a new page to your form (after adding a new row), you get a reference to that new page which you can then use to populate fields that it contains.

    In your particular situation, you need to pass information from fields within a row in the summary table to fields on a detail page within your form. What you could do is use a script object which contains a variable to which you assign the new instance of the detail page that’s associated with the summary row that gets added. Putting the reference in a script object will let you “remember” what that instance was as the filler proceeds to fill the fields in the summary row. Then, each field would have a script on its Exit event which would refer to the variable in the script object and use it to access the pertaining fields in the detail page it references in order to set values (each field sets the pertaining detail page field’s value).

    You can create a script object simply by right-clicking on the root subform (usually named “form1”) in the Hierarchy palette and choosing the “Insert Script Object” command. You then create a variable simply by declaring it like this:

    var oDetailPageRef = null;

    And you assign it a value like this (likely from a button you use to add a new summary row and detail page, assuming you named the script object, “MyScriptObj”):

    MyScriptObj.oDetailPageRef = _DetailPage.addInstance(0);

    Now this is an extremely simple implementation which may already have few usability flaws depending on how elaborate your form is. For instance, can the filler change the summary information on the associated detail page and, if so, do those changes have to be replicated back to the pertaining summary row? If that’s the case, then you need to synchronize the fields on the summary row with the fields on the detail page and this gets more complicated. Here’s a hint: You could use an 2D array as your script object variable in order to associate a summary row reference with a detail page reference. The array would associate an index with an array of two object references such that the index of the detail page or summary row would correspond with the index in the array which is associated with the sub-array that contains the references to the other object. I’m happy to elaborate on this if it’s what you would like to do.

  18. MK on May 14th, 2007

    Hi Stefan,

    Thanks for the help on this, it is greatly appreciated! Please pardon my lack of knowledge on this subject matter and my (probably) very basic questions. I’ve followed what you have advised above – I’ve created a script object (InstanceCnt) within which I have declared the following:

    var oDetailPageRef = null;

    In my “add instance” button, I have added the following code:

    InstanceCnt.oDetailPageRef = _page2.addInstance(0);

    To double check that the oDetailPageRef variable is getting populated, I outputted the variable value to the console and got the following:

    [object XFAObject] (Which I assume is correct).

    Now, just to show my lack of experience and knowledge, I’m stuck at how to reference the variable to populate the fields (referencing things seems to be my nemesis). You state in your previous answer that “each field would have a script on its Exit event which would refer to the variable in the script object and use it to access the pertaining fields in the detail page it references in order to set values…”.

    As a result, I’ve tried to reference the variable a number of ways and keep getting an error stating that what I have referenced has no properties. So, I’m guessing that I am not correctly referencing the variable and associated field in the details page in order to set its value to the corresponding field in the summary table. What is the proper syntax to accomplish this?

    Thanks again for the help!

  19. MK on May 14th, 2007

    As a followup to the second half of your response to my original question, I would be very interested in how to synchronize the forms more closely. I’m not sure that I have the technical abilities yet to incorporate it into my form (see my previous post), but at the same time new insights are always a good thing to learn…

    Thanks.

  20. Stefan Cameron on May 15th, 2007

    MK,

    It looks like you’re off to a good start. Before we takle synchronization between the summary rows and detail pages, let’s concentrate on getting the detail pages populated with the summary rows (one way).

    What I’m thinking is that each field in the summary row would have JavaScript like this on their Exit events (which get executed when the focus leaves the field — when you tab out or hit the Enter key):

    InstanceCnt.oDetailPageRef.{FieldName}.rawValue = this.rawValue;

    where {FieldName} is the name of the field within the detail page whose value should get the same value as the field into which the user has entered a value.

    Let me know how you do with this. Hopefully it works and we can proceed to the synchronization.

  21. MK on May 17th, 2007

    Stefan,

    Thanks again – this site is great! Okay, in the interim, I started playing around with some advice you had given another user, along with the advice you’ve given me. In the process I noticed something wierd – Acrobat gives me the “no properties” error when I reference (just as you showed above) the variable if I am referencing off of the container that has “Flow Content” (i.e., page 2). However, if I put a subform on page 2 of the same size as the margins, put my content there, and have that subform called by the instance manager – the referencing works beautifully. I’m not sure I explained that correctly – so let me show you my code:

    1. I added a field asking for how many instances I want (TextField1)
    2. On that field’s exit, I put the following code:

    var j = TextField1.rawValue
    _summary.setInstances(j)
    page2._Details.setInstances(j) // you’ll note that I’m not increasing the number of instances for page 2, but for a subform in page 2
    for (var i = 0; i

  22. Stefan Cameron on May 18th, 2007

    MK,

    Could you re-post your last comment? Some of your script got cut-off and I’m having difficulties understanding exactly what you’re trying to do without proper context.

  23. Dan on May 22nd, 2007

    Hi Stefan, I have a little challenging task my work gave me, I work at a church and they wanted a new registration form and wanted to do it in .pdf. I have already created the form, but I can’t get it to work like they would like it. The form is a family registration form where they add general family information and then there is mini boxes where they can fill out information per member. The church wan’t certain fields to be required, now obviously if there is only 3 memebers and there are 6 available spaces, it is not going to allow them to submit it unless thoes other fields are filled out. I found out about the way you can make a subform repeat, and that could work, but I don’t exactly understand how, especially in a example I found it had the subforms going down below, and I need them to start on the left and go to the right and continue on a second page if needed. I hope you can help and if you would like to see what the form looks like so you can get a better idea of what I am talking about please let me know.

  24. MK on May 22nd, 2007

    Stefan,

    I thought it may not have gone through – below is my submission:

    Thanks again – this site is great! Okay, in the interim, I started playing around with some advice you had given another user, along with the advice you’ve given me. In the process I noticed something wierd – Acrobat gives me the “no properties” error when I reference (just as you showed above) the variable if I am referencing off of the container that has “Flow Content” (i.e., page 2). However, if I put a subform on page 2 of the same size as the margins, put my content there, and have that subform called by the instance manager – the referencing works beautifully. I’m not sure I explained that correctly – so let me show you my code:

    1. I added a field asking for how many instances I want (TextField1)
    2. On that field’s exit, I put the following code:

    var j = TextField1.rawValue
    _summary.setInstances(j)
    page2._Details.setInstances(j) // you’ll note that I’m not increasing the number of instances for page 2, but for a subform in page 2
    for (var i = 0; i

  25. Stefan Cameron on May 24th, 2007

    Dan,

    Sounds like an interesting project!

    One very important thing to note about repeatable subforms is that they must be contained in a flowed container (another subform with it’s “Content” property, on the Subform tab of the Object palette, set to “flowed”). Once a subform is flowed, you can then specify the Flow Direction property to either be “Top to Bottom” (the default where new instances appear below existing ones) or “Western Text” (where new instances appear to the right of existing ones until there’s no horizontal space for new instances, at which point new instances start appearing on subsequent “lines” below existing ones).

    “Western Text” flow sounds like that you’re looking for in your form. Have a look at this sample in response to a question posted on my Scripting Table Columns article. (You’ll need Designer/Acrobat 7.x to edit/run it.) The sample is using a flowed container subform (“MockTableSF”) to act as a table column container and a repeatable subform (“ColumnSF”) which acts as a column of which many instances may be added from left to right by clicking on the “Add Column” button above.

    Hopefully this sample form gives you some inspiration for your form!

  26. Stefan Cameron on May 24th, 2007

    MK,

    Unfortunately, it didn’t go through again. I think the problem is the < bracket in your “for” loop statement. Try changing the angle bracket to “&lt” (and other instances if they exist), then post your comment. That’ll prevent my submission form from thinking the angle bracket is the start of HTML mark-up.

    Sorry about that! Hopefully it’ll work next time…

  27. Brent on May 24th, 2007

    Hi Stefan,

    I’m trying to create a dynamic table which will be displayed to the user when the form loads. Let me back up a bit first though. The user will select which columns s/he wants displayed on an .asp page, which will then write the names of those columns as csv string into a database field. Now, when the form loads, I’m using the js split function to put those columns into an array. Then I’m looping through the array and adding columns to the table according to what the user has selected.

    The js code looks something like this:
    (for now, I’m following your example on http://forms.stefcameron.com/2006/10/28/scripting-table-columns/)

    var myColumns = “Col0Name,Col1Name,Col2Name,Col3Name,Col4Name”;
    var arrMyColumns = myColumns.split(“,”);

    for (i = 0; i < arrMyColumns.length; i++)
    {
        //Create a New Column
        Table1.HeaderRow._Col3SF.addInstance(0);
        Table1.Row1._Col3SF.addInstance(0);
    }

    So, up to this point, the proper amount of rows are written, but the values in the header columns are all the same. Is there a way I can reference the instance I have just added and then change the header value for that particular column? In my case, the value would be arrMyColumns[i].

    Thanks a lot!
    Brent

  28. Dan on May 24th, 2007

    Stefan,

    Thanks a lot for the info it help a whole lot. I have one more question thought, how would I add a button that would remove the last subform, like say a member accidentally clicked the button and didn’t need to.

    Thanks

  29. Stefan Cameron on May 29th, 2007

    Brent,

    Cool project! All you should have to do in order to set names into your header column fields is to capture the reference to the new instance returned from the “addInstance” method into a variable and then access the field(s) it contains.

    For example, let’s say the “Col3SF” column subform contains a text field, named “ColNameTxt” which has a value set to the column name. In order to set the ColNameTxt field’s value to the pertaining column name from the arrMyColumns array, you would do the following:

    for (i = 0; i < arrMyColumns.length; i++)
    {
        //Create a New Column
        var oNewCol = Table1.HeaderRow._Col3SF.addInstance(0);
        oNewCol.ColNameTxt.rawValue = arrMyColumns[i];
    
        Table1.Row1._Col3SF.addInstance(0);
    }
  30. Brent on May 29th, 2007

    Thanks a lot Stefan! It makes sense and works perfectly.

  31. Stefan Cameron on May 29th, 2007

    Dan,

    You would simply add a new button object to the form next to the “Add Column” button and use the “removeInstance” method of the Instance Manager to remove the last instance of the “ColumnSF” subform (which can be determined by using the Instance Manager’s “count” property):

    if (MockTableSF._ColumnSF.count > 0) // then there's an instance you can remove
        MockTableSF._ColumnSF.removeInstance(MockTableSF._ColumnSF.count);
  32. Brent on June 4th, 2007

    Hi Stefan,

    I ran into another problem along the way. The code you gave me to build a dynamic header row worked perfectly, but when I try to add a new row I run into trouble. I can get as far as adding the actual row itself, but I can’t seem to add an instance of the cell inside the new row. So for example, the following will build both the header row and row 1 just fine:

    var myColumns = “Col0Name,Col1Name,Col2Name,Col3Name,Col4Name”;
    var arrMyColumns = myColumns.split(“,”);

    for (i = 0; i &lt arrMyColumns.length; i++)
    {
    //Create a New Column
    var oNewCol = Table1.HeaderRow._Col3SF.addInstance(0);
    oNewCol.ColNameTxt.rawValue = arrMyColumns[i];

    Table1.Row1._Col3SF.addInstance(0);
    }

    When I try to build the second row, though, it looks like one long row with no individual columns. I can build the third row with the following code:

    Table1.Row1.instanceManager.addInstance(0);
    or
    Table1.Row1._ColSF.instanceManager.addInstance(0);

    When I try to split that row up (like how the header and row are divided), I run into problems. If I try

    Table1.Row1._Col3SF.addInstance(0);

    I just end up adding another column to the table. So my question is, “How do start adding cells in row 2 to start making a complete row?”

    Thanks a lot,
    Brent

  33. Stefan Cameron on June 5th, 2007

    Brent,

    I think the problem you’re running into is that you aren’t working with the new instance of the row that you’ve added — you’re actually still working with the first instance because whenever you’re using

    Table1.Row1.

    it’s the same as saying

    Table1.Row1[0].

    when you’re actually wanting

    Table1.Row1[1..n].

    If I’m following you correctly, then I think you have a table, named “Table1”, which contains two rows, named “HeaderRow” and “Row1”, where “Row1” is repeatable (via the “Repeat Row for Each Data Item” property on the Binding tab of the Object palette) and contains a single column, named “Col3SF”, which is also repeatable (via the instructions I give on my article on script table columns).

    You are then able to add columns to the HeaderRow and the first instance of the Row1 row but you’re now stuck when it comes to adding columns to a new instance of the Row1 row, correct?

    If so, then I think it’s a matter of getting a reference to the new instance of the row that’s returned by the addInstance method:

    var myColumns = "Col0Name,Col1Name,Col2Name,Col3Name,Col4Name";
    var arrMyColumns = myColumns.split(",");
    
    // Let's say you're wanting 3 rows under the header row. You'll first need to add 2 more instances of Row1:
    Table1._Row1.addInstance(0);
    Table1._Row1.addInstance(0);
    
    for (var i = 0; i < arrMyColumns.length; i++)
    {
        //Create a New Column
        var oNewCol = Table1.HeaderRow._Col3SF.addInstance(0);
        oNewCol.ColNameTxt.rawValue = arrMyColumns[i];
    
        // As you loop through the items in the array, you'll need to access the new instances of Row1.
        //  You can do this by using the resolveNode method in JavaScript.
        for (var j = 0; j < Table1._Row1.count; j++)
        {
            // get Row1[0], then Row1[1], ..., Row1[j-1]
            var oRowInstance = Table1.resolveNode("Row1[" + j + "]");
    
            // add a new column to the instance of the row for each column added to the header row
            oRowInstance._Col3SF.addInstance(0);
        }
    }

    Give that a try and let me know how it goes.

  34. Brent on June 6th, 2007

    Stefan,

    You’re a genius! I’ve been struggling with this for the past few days, and I can’t believe how simple you made it. Anyway, the code works great. Thank you so much for your help.

  35. Toylar on August 2nd, 2007

    Hi Stefan,

    I am wondering if it’s possible to count the number of instances on a page so I can acess the first instance on the page and the last instance on a particuler page. What I am trying to do is…is I have a text field that is repeatable. This text field has paragraph numbers in it. I want to be able to get the first instance on the page and the last instance on the page and concatanate them together so that it tells me what paragraphs are on that particular page. For instance page 1 one can have 3 subforms with paragraph numbering of 1, 1.1, and 1.2…then when it is concatanted together says “Paragraph 1 through 1.2” and then on the second page have 5 subforms say 1.3, 2, 3, 4, 5 “Paragraph 1.3 – 5” etc

    Thanks,

    Tyler

  36. Stefan Cameron on August 17th, 2007

    Toylar,

    Sounds interesting. What I can suggest is that you use the xfa.layout.page method to determine which instances of the subform are on specific pages. Using the xfa.layout.pageCount method, you can determine the number of pages in the entire form. If you combine the two, and make use of the subform’s Instance Manager’s count property, you might be able to achieve what you’re trying to do.

    For example, it might look something like this in JavaScript (note that I have not tested this script so I cannot guarantee that it will work):

    var nTxtPage = xfa.layout.page(this); // 1-based page index of page that contains "this" instance of the text field
    var nSFCount = _ParaSubform.count; // number of instances of the repeating paragraph subform throughout the form
    
    var sFirstParaNumber = null;
    var nLastParaSFOnPage = -1; // index of last instance of paragraph subform on same page as text field instance
    
    for (var i = 0; i < nSFCount; i++)
    {
        var oSF = this.parent.resolveNode("ParaSubform[" + i + "]"); // get a reference to the i'th instance of the paragraph subform
        var nSFPage = xfa.layout.page(oSF); // page number of the instance
    
        if (nSFPage == nTxtPage)
        {
            if (sFirstParaNumber == null)
                sFirstParaNumber = oSF.ParaNumberField.rawValue; // get first paragraph number (but not subsequent paragraph numbers)
    
            nLastParaSFOnPage = i; // remember index of last paragraph (instance of the repeating subform) on page
        }
    }
    
    if (sFirstParaNumber == null)
        this.rawValue = "there are no paragraphs on this page";
    else
    {
        var oLastParaSF = this.parent.resolveNode("ParaSubform[" + nLastParaSFOnPage + "]");
    
        // set text field value to "Paragraph X through Y" using first paragraph number found in FOR loop and
        //  paragraph number from last instance of paragraph subform on same page
        this.rawValue = "Paragraph " + sFirstParaNumber + " through " + oLastParaSF.ParaNumberField.rawValue;
    }
    

    This script should be placed in the text field's Layout:Ready event so that the "this" keyboard refers to a particular instance of the text field (assuming there's one per page in the form, according to your description). You should be aware that the Layout:Ready event is fired every time something causes the form to need re-rendering which means it may fire very often. You might need to use a boolean variable to guard against executing it too often. Also, this script assumes that the repeating paragraph subform and text field objects are in the same scope. Otherwise, you'll need to add the necessary SOM paths to access the paragraph subform from the scope of the text field.

    Please let me know if it helps!

  37. John on August 17th, 2007

    Interest point we just discovered with reader v 8.

    We have had a script that has been working fine in reader 7 where we have a table of data in a contract that is filled in from a data input page.
    Each line in the table is a subform that is added using instanceManager.addInstance() but before this is done all existing subforms (table rows) were removed in a loop using instanceManager.removeInstance(int index). This is in case changes were made on the input page they would not be just appended but updated.

    This worked fine in ver 7 but we just found that in ver 8 the script failed at the first instanceManager.addInstance(). My guess is that once the last instance of the subform has been removed by instanceManager.removeInstance(int index) then the subform is no longer there to add an instances of.

    The work around was to remove all but index 0 in instanceManager.removeInstance(int index) and then after all the new instances have been added using addInstance() then remove instance 0

  38. Karl on February 25th, 2008

    Adobe LiveCycle Designer 8

    Basically it is a list of several entries, each row with a minus-sign to remove the row and a plus-sign to insert a row AFTER the row the plus was pressed at.

    The minus works fine and as long as rows are added by addInstance at the bottom the plus is also fine.

    The problem starts when inserting rows somewhere in the middle. I tried this with insertInstance and addInstance+moveInstance and everytime an instance gets added, but the content of all the other rows gets cleared (and also in another flowing subform with instances to add and remove).

    At time the code looks like this:

    r = Allgemein.Seite1.Mitversicherte.Liste.Eintrag.instanceManager.count;
    s = this.parent.index;
    Allgemein.Seite1.Mitversicherte.Liste.Eintrag.instanceManager.addInstance(false);
    Allgemein.Seite1.Mitversicherte.Liste.Eintrag.instanceManager.moveInstance((r-2), s);

    I have no idea, why all the other fields get cleared. Do you have an idea?

  39. Stefan Cameron on March 3rd, 2008

    Karl,

    Unfortunately, I don’t think I can tell what’s going on. There’s nothing that I can see with the code you’ve provided that would be causing data in all the other instances to be cleared.

  40. Adam Frey on March 10th, 2008

    I have looked endlessly on the web and cannot find an answer to this question. I know I must be overlooking something.

    I essentially have a line item subform. It is nothing too complicated. The only interesting thing is that I import the products, units, unit prices, etc. from a pre-made xml.

    Well everything goes smoothly. I import the price list, add the necessary lines for each line item to be added, I total up the price correctly, I even delete erroneous line items well.
    But if the users saves the form and reopens it, all instances except for the first one are gone. I’m bashing my head because it seems like this is a simple fix but I have checked every property and it all seems correct.

    Please advise. By the way, best livecycle resource. I wouldn’t have gotten the form this far without this blog.

  41. Mark J on March 12th, 2008

    I thought maybe i could post a issue since it seems to be similar to some of the issues listed here. I’ve built a form with 4 seperate tables. Each table has 2 header rows, both are grouped together to act as one. I’m using the addInstancemanger to add rows and all that is working great. All this is on a single page and i can span onto multiple pages but for some reason if i add enough rows from any one of the tables to create a 3rd page none of the header rows appear like they do on the 2nd page. I’ve been pulling my hair out on this one for the last couple of days, it doesnt make sense to me that the header rows would paginate onto the 2nd page but not the 3rd, 4th, 5th, etc..

    MJ

  42. Stefan Cameron on March 12th, 2008

    Adam Frey,

    Thank you for the compliments. I’m glad you find my blog useful!

    Normally, when you’re using straight data binding, any data that you import into the form will be saved when you save it in Acrobat (or Reader if the form has been Reader-Extended).

    If you’re using script to add/remove instance of your line item subform and you’re processing the XML data manually, then this could explain what’s happening. What you need to do is either add more script to figure-out if you need to re-initialize the form to the previous state (but then I suppose this implies you stored the imported XML as data in a hidden field somewhere in the form) or you could try using the “Preserve scripting changes to form when saved > Automatically” option in the “File > Form Properties > Defaults” dialog (available at least in Designer 8.1, perhaps in 8.0).

  43. Adam Frey on March 13th, 2008

    I worked around the issue I listed above. I doubt its the “correct” fix, but it works.

    I set the “initial value” of the subform to a large number (I called it 40). I tracked how many line items the user added in a hidden field. And on the initalize function for the form I used the setInstances() function to whatever the hidden field count is. It works, I’m happy, thanks.

  44. Stefan Cameron on March 22nd, 2008

    Mark J,

    I’m afraid I can’t tell what could be happening based on your explanations. I would need a little more detail about your form. For instance, is the table inside a series of flowed subforms all the way up to the root subform? Are the headers grouped together as a table section? What do you mean by “none of the header rows appear like they do on the 2nd page”?

  45. Mark J on March 27th, 2008

    Thanks for your reply Stefan, I’ve found so much useful information from your post’s on all your Blogs. Sorry for my vague question, I’m still learning.

    The structure of the form is:

    Header Layout
    There is a header subform that is set to positioned and it contains several text fields.

    Table Layout
    I have 4 tables with 2 header rows on each and 13 columns (first header row for title of table and add row button, second row is used for column titles). The 2 table headers for each table are grouped as a table sections.

    Structure:
    Main page(subform-holds all page elements).table(subform-holds all tables).table1(subform-only holds table1).table1(table) — Inside each table is 1 header group for the 2 header rows and 1 table row for the data along with a remove button to remove specific instances from the table. The remove button is wrapped in a subform but it is the only table field that is wrapped in a subform on the “1 table” row.

    The remaining tables all match this structure with the exception of the name changes from table1 to table2, etc.

    Footer Layout

    At the bottom of the form is a positioned subform that contains a static text field along with a text and date field. These items get pushed down as more rows are added to the tables.

    It was by chance that i even noticed this issue but my form is pretty much packed on a single landscape page and as soon as i add a single row to any table a new page is created which is all great but lets say i just kept hitting the add button on table 1 and it keeps adding rows as it should. The table 1 header rows are repeated onto the second page as expected but if i continue to hit the add button and a 3rd page is created, none of the header rows get created on the 3rd page for table1. I can only see them for the first 2 pages of the form. Now if i stop adding rows to table1 and start adding rows to table2 it will do the same thing and duplicate the header group for the next 2 pages then it stops duplicating the header row onto each page after 2 more pages are created.

    I can only guess that there is a setting that I’m missing, that indicates the number of times a table header row can be repeated or in my case a header group.

    thanks for you reply and help on this,

    Mark J

  46. Stefan Cameron on April 3rd, 2008

    Mark J,

    Sorry for the delay. We’ve been heads-down lately working on some code.

    You’ve provided a very good explanation of your form and I was able to replicate the issue you’re having. It looks like you’ve uncovered a bug where if the object being used as an overflow leader (which is what the table “header” section becomes with respect to the table when you choose to have the section repeat on subsequent pages) has flowed content (in this case, two header rows are flowed within the table section), the rows end-up positioned on top of one another on subsequent pages. It’s strange that it only happens after the second page for tables but I think the underlying issue is the same.

    My only suggestion at this point is that you replace the section and two header rows with a single row, make all cells span the 13 columns, make the cell into a single subform with positioned content, make that row the height of two rows and insert the necessary form objects to get your table title, column titles and button into it. Then mark that single row as a header row and see if it will repeat correctly on all pages.

    As for the bug, I will definitely report it so that it gets fixed in a future release. Thanks!

  47. Stefan Cameron on April 4th, 2008

    Mark J,

    I inquired about this table header bug and it seems there’s no bug after all: You should make sure that your section is set to repeat on subsequent pages (via the “Object palette > Pagination tag”) as well as making sure that it’s repeatable (by checking the “repeat for each data item” option on the “Object palette > Binding tab”). Doing this fixed the issue in my form.

    As for the issue with overflow leaders having flowed content, that’s apparently not yet supported. Only overflow leaders with positioned content are fully supported at this time.

    Give it a try and let me know.

  48. Hello, on June 5th, 2008

    I don’t know where to write about my problem, so I will try here. I have a repeating subform, with dynamic captions, for both romanian and english language. The problem is, that when the second instance of the subform is generated, the captions are not loaded from the XML file that has them, but they present the text that was entered in designer when the form was first drawn. Can you help me? Thank you!

  49. Stefan Cameron on June 8th, 2008

    Hello,

    How is the second instance of the subform being generated? Are you using a button’s click event and executing the “addInstance” method of the subform’s Instance Manager? If that’s the case, are you calling “addInstance(1)”? The “1” as the parameter will cause new instances to be merged with any data that has yet to be merged into the form. If you’re calling “addInstance(0)”, any remaining data will not be merged into the new instance.

    You mentioned you were using dynamic captions. I assume you’re doing this with Designer’s Dynamic Property feature. Consider the following data:

    <info>
    	<instance>
    		<caption>cap 1</caption>
    	</instance>
    	<instance>
    		<caption>cap 2</caption>
    	</instance>
    	<instance>
    		<caption>cap 3</caption>
    	</instance>
    </info>

    If you had a repeatable subform with a text field inside of it and you wanted the text field’s caption to be “cap 1” for the first subform instance, “cap 2” for the second instance and “cap 3” for the third instance, you would define the subform’s binding as “instance[*]” and the text field caption’s dynamic property binding simply as “caption”.

  50. troy seiler on June 13th, 2008

    Stefan,

    Your blog is a great resource and has helped me tremendously to create a working dynamic form. Could you please help with a problem I have with a repeating subform using instance manager.

    I have a form with two master pages (‘portrait’ and ‘landscape’). the majority of the form is laid out as ‘portrait’. On these pages the instance manager works just great to repeat subforms.

    I have one dynamic subform on the ‘landscape’ master page. After this subform, the remainder of the form is set up on ‘portrait’ master pages.

    So, here’s the problem: when I use the instance manager and repeat the ‘landscape’ subform (which then overflows to another page), the subform is generated on a ‘portrait’ page, instead of a ‘landscape’ page.

    In the pagination for this repeating ‘landscape’ subform, I have the “Overflow” set to “Go to Page’landscape'” . However, when I test the form, it still generates the subform onto a ‘portrait’ page. I have tried many combinations of pagination settings and none have produced the desired outcome: the subform repeated onto a ‘landscape’ master page.

    I am new to livecycle. Is there something here I am overlooking? Any help would be greatly appreciated

  51. Stefan Cameron on June 14th, 2008

    Troy Seiler,

    Normally, flowed content such as repeatable (dynamic) subforms is placed on body pages, not master pages. I don’t think master page content flows quite the same way. You should be able to move those subforms onto the body pages and use the “Object palette > Pagination tab” to ensure that they get placed on the correct master pages (“landscape” vs “portrait”) and then use the “Overflow” property as you’ve set it. That should get the subform placed on the “landscape” master page to overflow correctly onto another instance of the “landscape” master page.

    About master pages vs body pages: Note that body pages contain the content that flows from one master page to another. A master page defines various properties about the pages onto which body content flows. Master pages can have one or more content areas which define where body content (on the body pages or “Design View” since Designer 8.0) flows. A new instance of a master page is created every time all the content areas on the current master page instance have been “filled” with body content from the body pages. You can dictate what master page is used for the next “physical” page (needed by the remaining flowed body content) by using the “Object palette > Pagination tab” (available for subform-based objects).

  52. troy seiler on June 16th, 2008

    Stefan,

    Thank you for responding so quickly. I don’t think I expressed my problem clearly. I have the pages configured as you suggest (on body pages, with master pages assigned using the pagination tab). It seems the problem lies with switching between ‘portrait’ and ‘landscape’ then switching back to ‘portrait’. The form works well when I place the ‘landscape’ body page at the end of the form (so it’s the last page). However, when the ‘landscape’ body page is sandwiched between two ‘portrait’ pages, a ‘portrait’ body page is generated for overflow, regardless of the configuration on the pagination tab.

    Here’s the simplified hierarchy of my form (this arrangement always generates a ‘portrait’ page, despite the pagination overflow being set to ‘landscape’):

    -Master pages
    -portrait
    -landscape
    -subform1(flowed)
    -Body page1 (pagination=portrait)
    -subform2(flowed)(repeated)
    -Body page2 (pagination=landscape;overflow=landscape)
    -subform3(flowed)
    -body page3(pagination=portrait)

    However, when I rearrange the hierarchy as follows, the form works properly (i.e. a landscape page is generated for overflow):
    -Master pages
    -portrait
    -landscape
    -subform1(flowed)
    -Body page1 (pagination=portrait)
    -subform3(flowed)
    -body page3(pagination=portrait)
    -subform2(flowed)(repeated)
    -Body page2 (pagination=landscape;overflow=landscape)

    I have found another post on a forum suggesting that this is a bug. However, I cannot find confirmation (or a fix) if this is the case.

  53. troy seiler on June 16th, 2008

    the hierarchy spacing was not preserved in my last post. To clarify, the body pages are below their respective subforms in the hierarchy, and ‘portrait’ and ‘landscape’ represent the two master pages

  54. Brad Gubanich on June 18th, 2008

    Stefan, you may be my last hope. Livecycle is new to me and I’ve been learning a lot in a short amount of time. I’ve exhausted my search online for help and I have come to you as a last resort. What I want to do seems so easy, but I’m having trouble repeating row data from one row to the next.

    I have a repeatable form that a user will fill out. I am able to add/delete a new row w/o a problem. Some of the fields in the rows will stay the same. I am trying to creat an instance of a new row, capture the data from one field (lglIdentity[0]) and paste it in the next (lglIdentity[1]) and so on and so on. here is my code:

    details being my table name, detail being my row name and lglIdentity being the cell to copy and past to the next row.

    var lglIdentity
    lglIdentity = details.resolveNode(“detail”).lglIdentity.rawValue;
    xfa.host.messageBox(“legal is: ” + lglIdentity, “Debugging”, 3) //this captures the object just fine

    var inst = details._detail.addInstance(1);

    var lglIdentity1
    lglIdentity1 = details.resolveNode(“detail[1]”).lglIdentity[1].rawValue;
    inst.lglIdentity1.value = lglIdentity

    This is only half the problem though. Eventually I will need to copy this data to multiple rows, depending on the number of rows. I’m assuming I’ll need some kind of loop, is that correct. How do I go about this all? I’m at a dead end right now.

    Is there something here I am overlooking? Any help would be greatly appreciated, thank you.

  55. Evan Buxton on June 25th, 2008

    Hello,

    I have a subform that includes an image field (the one you click to load your own). Let’s assume there are two instances of the subform with different images in each. When I insert an instance between the two the image in number 2 (now 3) disappears. If I remove instance 2, instance three (the original 2) comes back with the image loaded. If I add an instance at the bottom everything stays copacetic. It is only when inserting between rows that the images disappears. How can I get the loaded image to travel with the moving instance?

    Thank you

  56. Stefan Cameron on June 25th, 2008

    troy seiler,

    I understand what you’re seeing looks like a bug but I can’t exactly pin-point whether it is or not. I am wondering, however, why you’ve structured your form as you have. Why did you put page subforms inside other subforms, as in

    subform1
        body page 1

    I would try removing those body pages and just using the subform1, subform2 and subform3 subforms (which I believe are parented to the root subform given the hierarchy you listed). You can make subform2 repeatable just like you could “body page 2”. Then set your pagination on the subforms and see if that fixes the issue. I have a feeling that the layout engine is getting confused on which master page should be used for the following subform (i.e. it finishes rendering “body page 2” with the landscape master page and then renders subform3 — which likely uses “following previous” as its pagination — and puts it onto the landscape master page even though the subform inside subform3 says that it should go on the portrait master page).

  57. Stefan Cameron on June 25th, 2008

    Evan Buxton,

    It looks like you’ve discovered a bug. Thanks for pointing this out. Fortunately, there’s a workaround in the meantime: After inserting the subform instance, force the form to be re-rendered using xfa.layout.relayout():

    _subform1.insertInstance(1);
    xfa.layout.relayout();

    What’s happened is that the image has, in fact, followed along with the instance of the subform however Acrobat/Reader hasn’t properly updated the form’s layout after inserting the instance in the middle. I’ll make sure this is reported appropriately.

  58. troy seiler on June 27th, 2008

    Stefan,

    I see your point. I believe I tried what you are suggesting, with the same results. But, I will play around with the hierarchy as you suggest and see if I can get this to work. Thanks for your input.

    Just to explain. . . and I apologize for making you pull the complete story out of me piece by piece. . .

    The reason I have the subform within a subform hierarchy is that I have designed the form with the ability to repeat a group of several pages AND to individually repeat a single page within the larger group. It is a very large form, and basically, I found it slightly easier to work with using the current hierarchy.

    -Troy

  59. Jeff Maughan on July 1st, 2008

    Stefan,

    I’m trying to create a form calculation that returns 20% of the sum of some other fields but has bounds on both ends at 200 and 450. Here is the code that i have thus far…

    if ((this.getField(“Provident_YSP”).value + this.getField(“Check_Total”).value – this.getField(“Appraisal”).value) * .2 > 450 )
    this.getField(“Superior_Lending_File_Fee”).value = “450”;
    else if ( (this.getField(“Provident_YSP”).value + this.getField(“Check_Total”).value – this.getField(“Appraisal”).value) * .2 < 200 )
    this.getField(“Superior_Lending_File_Fee”).value = “200”;
    else
    this.getField(“Superior_Lending_File_Fee”).value = (this.getField(“Provident_YSP”).value + this.getField(“Check_Total”).value – this.getField(“Appraisal”).value) * .2;

    The problem i am running into is that it doesn’t work after i close and reopen the form. It does show the minimum 200 value, but doesn’t update when the preceding fields are changed. Could you help me out with this?

    -Jeff

  60. Stefan Cameron on July 12th, 2008

    Jeff Maughan,

    First, the calculation should be inside a Calculate event on the field that needs to show this percentage value.

    Second, you’ve scripted your calculation in such a way that you’re circumventing XFA in order to get field value. I believe this is bypassing the Calculation Engine which means it won’t be able to establish dependencies on this field to other fields such that when their values change, this calculation is run again.

    Try it like this (where you access field values from within XFA):

    if ((Provident_YSP.rawValue + Check_Total.rawValue - AppraisalrawValue) * 0.2 > 450)
        Superior_Lending_File_Fee.rawValue = 450;
    else
        ...

    where the pattern of

    this.getField("FieldName").value

    becomes

    FieldName.rawValue
  61. Stefan Cameron on July 26th, 2008

    Brad Gubanich,

    I just found your comment from last June still awaiting moderation. The email notification must’ve been flagged as spam and got deleted.

    In case you’re still wondering how to do this, based on the code you quoted:

    details being my table name, detail being my row name and lglIdentity being the cell to copy and past to the next row.

    var lglIdentity
    lglIdentity = details.resolveNode(“detail”).lglIdentity.rawValue;
    xfa.host.messageBox(“legal is: ” + lglIdentity, “Debugging”, 3) //this captures the object just fine

    var inst = details._detail.addInstance(1);

    var lglIdentity1
    lglIdentity1 = details.resolveNode(“detail[1]”).lglIdentity[1].rawValue;
    inst.lglIdentity1.value = lglIdentity

    you would simply execute this script when adding a new instance of the “detail” row (assuming I understood what you’re trying to do):

    var newInst = _detail.addInstance(0);
    if (_detail.count > 1)
    {
        // get value from preceding instance
        newInst.lglIdentify.rawValue = details.resolveNode("detail[" + (_detail.count - 1) + "]").lglIdentity.rawValue;
    }

    The above code will determine if there’s more than 1 instance after adding one. If there are at least 2 instances, there’s a preceding one from which the value of lglIdentity can be retrieved. The code proceeds to get that value by crafting an expression like “details[x]” which it gives to the “resolveNode” method of the “details” table object (where “x” is the instance number of the preceding instance). From there. it gets its lglIdentity field value and assigns it to the new instance.

  62. Mark on September 29th, 2008

    Stephan,

    Thanks for the great blog i have found many useful solution to issues and found it to be a great resource for my education in form design.

    I’ve be challenged with something that i though would be an easy solution but cant for the life of my figure this one out. My employer has asked me to create a radio button set at the top of a form that repeats on to each page but each repeat must retain the value of first radio button set. I put the radio button set on the master page and repeats as expected but doesn’t retain any of the values from the first set on the new page. I’m thinking i should have put the radio button set in a subform and used the instancemanger to copy the previous set while maintaining the same values if that is possible. I would appreciate any incite into this that you could offer a rookie. 🙂

    thanks,

    Mark

  63. Mark on September 29th, 2008

    I figured it out. So simple i feel stupid but setting the radio button set to Global on the Binding tab gave me this intentionality.

    thanks,

    Mark

  64. Laura on October 9th, 2008

    Hi Stephan – I’ve found your blog very helpful. Thank you! You seem to be very willing to help, so I’m gonna ask … I can’t get my dynamic form and instancemanager to work.
    Here is what my form looks like:
    PurchaseAuthorizationForm
    >(Master Pages)
    >page1 (flowed subform)
    >>PrintButton
    >>Title (positioned subform)
    >>AuthorizationAgreement (flowed subform)
    >>>items (flowed subform)
    >>>>item (positioned subform, on Binding tab the ‘Repeat Subform’ is checked)
    >>>>>2 text fields (that I want to repeat)
    >>>>addAnotherItem (flowed subform)
    >>>>>btnAdd (the button that runs: xfa.PurchaseAuthorizationForm.page1.AuthorizationAgreement.items.item.instanceManager.addInstance(true);)
    rest of the form is incomplete

    What I want to happen: when user clicks the btnAdd, the item subform and it’s two text fields are repeated.

    What am I missing?!?!?!?! This is driving me crazy. I’m using the Allegiance multiBeneficiary sample as an example with no luck.

    THANKS!

  65. Stefan Cameron on October 19th, 2008

    Laura,

    You’re welcome!

    Are you running this script in JavaScript or FormCalc? Are you seeing any error messages? If you’re using FormCalc, you should get a message box with an error. Otherwise, you’ll have to look in the JavaScript Console in Acrobat to see if there are errors (when previewing your form in Designer, click on the form and then press “Ctrl + J” to show the console).

  66. Hilary on November 6th, 2008

    Stefan,

    Great to see your site, you are really helpful!

    I have a growing table, user can press Add button to add rows while each row can have different height (the cells allow multiple line) just depends on what they input.
    What I wanna get is the whole table’s height, (I cannot get the height for flowed subform). So instead I decide to add up all the rows’ height for that.

    Although I can get the total row counts for it:
    var oRow1 = xfa.resolveNode(“Subform1.Table1.Row1”);
    rowcount = oRow1.instanceManager.count;

    I have problems referencing each instance row’s cell
    (suppose the body row consists of a Name and Address field):
    I think somehow the code should look like:
    oRow1.Name[0].h (for row 1)

    Thanks a lot.

  67. Stefan Cameron on November 10th, 2008

    Hilary,

    What you actually need to do is get the layout height of the table after the user has entered their text. It’s only once the form has been re-rendered that you can get the true height.

    Since your table’s height varies, if you were to get the height of the table, it might say “5in” when it’s actually 11.483 inches tall after all the text has been entered into all of the rows. Same goes for an individual row: It’s height might be “3in” but it’s set to grow in height to accommodate multiple lines of text in the fields it contains. The actual row height might be 5 inches for a particular row.

    The way you get the rendered height of the table or a row is by using the xfa.layout.h() function which takes a reference to the object whose height you want to retrieve.

    To get the height of the table, you would call

    var tableHeight = xfa.layout.h(Table1, "in");

    The second parameter represents the units in which the height should be returned. You can specify “in”, “pt”, “cm” and “mm”.

    To get the height of each row, you would do this:

    var totalHeight = 0;
    
    for (var i = 0; i < Table1._Row1.count; i++)
    {
        var row = Table1.resolveNode("Row1[" + i + "]");
        totalHeight += xfa.layout.h(row, "mm"); // calculate total height in millimeters
    }

    Note, however, that you can't use the xfa.layout.h() function until the form has been rendered which means you would typically use it in a Click event initiated by the user or you would use it in the LayoutReady event which is fired after the form has been rendered.

  68. Hilary on November 19th, 2008

    Stefan,

    Thanks for your reply, I can finally handle it.

    Yet there is another problem I encounter now.
    My form is a dynamic one with data flowing. After filling the form, it has to be rendered such that a new layout comes out for printing.

    It is ok if I separate these actions:

    0) Originally the first original page is displayed for filling data. Data may span on next page. While pages used for rendering a new layout will be hidden at the beginning.

    1) There is a Preview button on the first of original page(s), and on click, scripts will run to hide the original page(s) while the new rendered page(s) become visible.

    2) On the rendered pages (read only), there is a Print button for printing which simply codes:
    xfa.host.print(1, “0”, (xfa.host.numPages -1).toString(), 0, 0, 0, 0, 0);

    3) There is also a Preview Close button so that on click, the fields of the rendered pages will be cleared and hidden, while the original pages will reappear again.

    However when I intend to use a Print button to combine all above steps (Print Preview -> Print -> Close Preview) in one script, when the print dialog comes out, it just display a blank page for printing.
    It seems that xfa.host.print occurs not on the right time.

    Any idea?

  69. Hilary on November 19th, 2008

    Hi Stefan,
    Just a bit of amendment on my post above, actually if I press the Print button, when the print dialog comes out, it only displays one page which is the 1st page of all rendered pages, so if there are totally 3 pages, it will miss out the two pages then.

    On debugging, I check out that xfa.host.numPages = 1 instead of should be 3.
    and when I intend to force printing all 3 pages using:
    xfa.host.print(1, “0”, “2”, 0, 0, 0, 0, 0); it becomes worse that it doesn’t prompt out a print dialog and seems stop running code.

  70. Stefan Cameron on November 19th, 2008

    Hilary,

    It could be a delay in the rendering of the pages for print as a result of your “print preview” script. You could try forcing a re-layout of the form just before you issue the xfa.host.print call:

    xfa.layout.relayout();

    The relayout() call should be blocking and should take place immediately which means that by the time your script executes the print() call, the pages should be rendered.

    If that doesn’t work, try having your button no do the “close preview” step to see if you can print what you want. If that works, then it’s the xfa.host.print command that is asynchronous and executes only after the script has finished executing, at which point your “close preview” step has removed all of the pages meant to be printed…

  71. Hilary on November 20th, 2008

    Hi Stefan,

    Thanks for your prompt rely but I had tried relayout() already that it doesn’t work. Taking out the “Close Preview” step can avoid the blank pages but only first page of the rendered pages got printed. It seems that xfa.host.numPages still equal 1 after relayout().

    Any other method that I can perform these steps in just one button?
    Preview (rendering) => Print => Close Preview

  72. Stefan Cameron on November 28th, 2008

    Hilary,

    I did a little bit of investigating and learned that the xfa.host.numPages property is only updated in an idle cycle which means that you’re hitting a timing issue: You can’t set the presence of the various pages and then immediately initiate the print command otherwise the idle cycle will not occur and xfa.host.numPages will report the old page count, not the new one. And xfa.layout.relayout() unfortunately does not update the page count either.

    So, you have to delay the call to xfa.host.print using the AcroForm app object, which is essentially Acrobat/Reader, and its setTimeOut() method.

    Here’s an example. In the “fill view”, you have a print button that has a Click event that does this:

    // set the print view
    PrintPage1.presence = "visible";
    PrintPage2.presence = "visible";
    FillPage.presence = "hidden";
    
    // let the idle cycle take place prior to initiating the print command so that xfa.host.numPages returns the correct page count
    app.setTimeOut('xfa.host.print(1, "0", (xfa.host.numPages -1).toString(), 0, 0, 0, 0, 0);', 500);

    It also has a PostPrint event that does this:

    // restore the fill view
    PrintPage1.presence = "hidden";
    PrintPage2.presence = "hidden";
    FillPage.presence = "visible";

    This does everything in one step like you were asking (note that I tested this with Acrobat 9 Pro).

  73. Hilary on December 9th, 2008

    Stefan,
    Thanks for your help indeed.
    Actually I have solved the problem by using:
    app.execMenuItem(“Print”);
    which seems that it does exactly what I want. Anyway I will try your solution as well.
    BTW, another question, how can I check a textfield is overflowed? I know there is the “full” event but I need to do other programming task through a button to determine the event.
    I know you can check if (event.fieldFull) in Acrobat but is it available in lc designer?
    Many thanks again.

  74. Stefan Cameron on December 10th, 2008

    Hilary,

    Before I answer your question about the field being full, does this field have a maximum character length and you need to do things when the maximum number of characters have been entered or does the field have its “Object palette > Field > Limit Length to Visible Area property” set and you want to do things when it becomes “full” in that sense?

  75. Bill on December 10th, 2008

    I have what I hope will be a simple question with a simple solution. I havea table (“Table1”) with header and footer rows as well as detail rows (“Row1[*]”). The detail rows relate to products that the form user can click on to order, and if the check box in the row is clicked, the fields for quantity and price are required fields. If the check box for a particular row isn’t clicked, then prior to printing the quantity and price fields are reset (so the totals won’t be fouled up) and the row is hidden.

    What I’d like to do is figure out how to check that if none of the rows are checked (i.e., the customer didn’t order any of that particular product, and thus there are no detail rows in the table), the balance of the table (the header and footer rows) are hidden too.

    It seems like using instanceManager in some fashion should be able to return a 0 or otherwise indicate that there are no detail rows in the table, but I’ll be hanged if I can figure it out.

    Any help greatly appreciated!

  76. Stefan Cameron on December 15th, 2008

    Bill,

    What you need to do is obtain the number of instances of Row1 that exist in Table1. You can do this by accessing Row1’s instance manager using the special instance manager object for Row1 that has an underscore prefix in its name: “_Row1”

    if (Table1._Row1.count == 0)
        Table1.presence = "hidden";
  77. hilary on December 16th, 2008

    Hi Stefan,

    The field does not have maximum number of characters limit but has “Limit Length to Visible Area property” set.
    Actually I wanna similate the situation that codes will run on event full but instead I have to do it on a program basis. ie. through some scripts on a button click.

  78. Stefan Cameron on December 20th, 2008

    hilary,

    I’m afraid I couldn’t find an equivalent to Acrobat’s event.fieldFull property on the XFA side. The only thing I can suggest is that you set a maximum character limit which you know won’t allow data entered to go beyond the field’s border (rather than limit the length to the visible area) and then compare the field’s rawValue length to the maximum character limit:

    var valueTypeNode = TextField1.value.oneOfChild;
    if (TextField1.rawValue != null)
    {
        if ((valueTypeNode.className == "text" && parseInt(valueTypeNode.maxChars) == TextField1.rawValue.length) || (valueTypeNode.className == "exData" && parseInt(valueTypeNode.maxLength) == TextField1.rawValue.length))
        {
            // TextField1 is full
        }
    }
  79. Chris on December 30th, 2008

    Hi Stefan,

    Love your blog!

    I have a form with 20 fields, and at the bottom you have the option to add another request. When the user presses this button a validation takes place to confirm the user has entered all the necessary information. If the user has, the 20 fields repopulate in the form and the add another request button moves down to accommodate the 20 fields.

    The problem I am having is when the user clicks on the add another request button again the validation script is ignored because (I think) it reads the first 20 fields (that are already filled in) and thinks everything is fine so it spits out another 20 fields. Now there are 60 fields and only the top 20 are validated.

    My question is how do I reset my validation to run again once a new set of 20 fields appears?

    My validation code is pretty simple. I got it off of an adobe sample and modified it for my form. Here is the link to the original validation code:

    http://livedocs.adobe.com/livecycle/8.2/acrobat_designer/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Adobe_LiveCycle_Designer_Help&file=001206.html

  80. Stefan Cameron on January 6th, 2009

    Chris,

    Unfortunately, the link you provided to the validation code sample brings me to the welcome page of the Designer LiveDocs site. The URL you want is the one at the bottom of the page next to the “Current Page” label.

    Based on what you described, I gather you have a repeatable subform that contains approx. 20 fields which are validated prior to a new set being added however it’s always the first set that’s validated, not the last one added. Sounds like you just need to make sure you’re referencing the correct instance of the repeatable subform. Something like

    var lastInstance = parentSubform.resolveNode("repeatableSubform[" + (_repeatableSubform.count - 1) + "]");

    would get you the last instance of the repeatableSubform inside the parentSubform…

  81. Hilary on January 12th, 2009

    Hi Stefan,

    Grateful for your responses, here another question (seems simple but just can’t figure it out!) I need to seek your help.

    I have a dynamic form that get several text fields, these text fields are expandable and can be input values that may span more than 1 page, however the values flow to next page while no caption will be shown for the field on the next and extra pages. (I want to have the caption shown for the field even if it overflow to extra pages)

    I have tried using table but if data for a row is more than one page, there will be strange look.

    I have also tried using overflow leader subform (containing the caption) for the field (inside another subform) but the caption only appears on the next page while get missing on extra pages. Anything wrong?

    Any ideas?

  82. Hilary on January 13th, 2009

    Stepfan,

    Just work it out by using table on my previous post, the header (like a caption for textfield) can be shown again when data flow on to next page.

    Another question then, is there a way to skip the print dialog box in app.execMenuItem(….)?
    I know a parameter can be set for xfa.host.print(….), but I need to use app.execMenuItem(”Print”) since I cannot use xfa.host.print for some reason.

  83. Stefan Cameron on January 15th, 2009

    Hilary,

    Well, there is the AcroForm Doc object’s print() method which has a “bSilent” parameter which can be set to true in order to suppress the dialog however I’m guessing that this parameter can only be set in a privileged/trusted environment due to security issues…

    In an event, you can get the event.target object, which is the AcroForm Doc object representing the PDF which contains the XFA form, and you can then execute its print() method.

  84. Hilary on January 16th, 2009

    Stefan,

    Thanks for your advice, I’ll give it a try.

    Another problem I encounter is about HeaderRow section of a table now (similar as the post replied by you on #47 above)

    I have a flowed table in the middle of a page (actually the whole form is flowed, ie. above and below the table subform are other flowed subforms)

    It has 2 header rows being grouped as a header section, (actually I need all the 2 header rows to be shown on pagination)

    When body rows are continuously added, the header section only appears on the 1st and 2nd pages, while lost from the 3rd page onwards. This could be solved by checking the “repeat for each data item” option on the (“Object palette > Binding tab) & check the include Header Row in Initial Page & Subsequent Page on the Pagination tab of the header section as suggested by you.

    However, all good things stop after I saved the form, and then reopen it, the form got very strange look as the header rows are repeating itself where the occurence seems to depend on the no. of data rows, I have tried many different set of options to set the header section as well as the two header rows but still cannot work.

    Then I try to use 1 header row without the header section.
    then the form is ok, I wonder if there is bug on header section or table handling more than 1 header row.

  85. Stefan Cameron on January 21st, 2009

    Hilary,

    I tried to reproduce the behaviour you were describing with the header section using Designer 8.2 and Acrobat 9 but was not able to. What version of Designer and Acrobat are you using?

  86. Hilary on January 23rd, 2009

    Stefan,

    Really thanks for your help, you are always around.

    I am using Designer 8.2 and Acrobat 9, just dunno why, anyway I have solved #84 by using another method.

    I am now facing another big problem:
    How to print a form that needs rendering without opening Acrobat Reader (ie. from Window explorer)?

    I have developed a pdf that needs further rendering after user fill in the form, I simply create a button called that will run scripts to render the form, print it out (I have to use app.execMenuItem(”Print”) instead of xfa.host.print as I have asked you at the post #68-72, the app.setTimeout doesn’t work for me) and finally close the rendered look and back to the original look as user press the button before.

    Once the pdf was saved, if I want to print it out later without opening Acrobat (ie. from Window Explorer), I work it out by adding scripts to render the form at the form’s “prePrint” event, the form did printed but only for the 1st page, seems like what you said xfa.host.numPages property is only updated in an idle cycle).

    I wanna figure out what events inside the form will be triggered if I simply clicked Print from Window Explorer.

    Any idea?

  87. Stefan Cameron on January 26th, 2009

    Hilary,

    I can only assume that all necessary events required to render the form properly for print, which should be the same as what you would see when you open the form, will fire so that it ends-up being printed correctly.

  88. Hilary on January 29th, 2009

    Stefan,

    I have tried to put the scripts for rendering my form on docReady event.

    By debugging xfa.host.numPages on prePrint event, when I print from Windows Explorer, my form did rendered but the no. of pages still remains 1 where I display xfa.host.numPages that shows 1 which should be 3 actually after rendering. The result is that only page 1 is printed.

    I try to explicity assign the actual value (ie. 3) to xfa.host.numPages after rendering the form but it doesn’t work since it still shows 1 on debugging. Is there any method to delay the printing such that xfa.host.numPages can first be updated to a correct value? I have tried app.settimeout but it doesn’t work.

  89. Eshwari on February 2nd, 2009

    I have to place images in a dynamic table. This table has one row and within that row I have placed an ImageField. I am trying to increment the table row at the initialize event of the page where the table is placed. How to assign values to the imageField when the rows are incremented… The images are read from a byte stream….rendering images are not a problem, but placing these images within the growing table row is not working.

    obj = xfa.datasets.data.Appraisal.Photos.nodes;
    for(i=0;i “<” obj.length;i++){
    form.pg_page1.sf_subForm1.tbl_Photos.Row1.instanceManager.addInstance(true);

    }
    Hope someone could answer my question….

    Thanks
    Eshwari

  90. Eshwari on February 2nd, 2009

    I am able to resolve the above issue using below scripting,

    obj = xfa.datasets.data.Appraisal.Photos.nodes;
    for(i=0;i < obj.length;i++){
    var oRowInstance = Appraisal.pg_ImageTesting.sf_ImageTesting.tbl_Photos.resolveNode(“Row1[” + i + “]”);
    oRowInstance.ImageField5.rawValue=obj.item(i).value.toString();
    oRowInstance.instanceManager.addInstance(true);
    }

    But somehow an extra rows is created. It tried to eliminate it with xfa.Appraisal.recalculate(); But still one extra row is created. Kindly let me know how to resolve this. Appraisal is the name of the form and pg_ImageTesting is a page.

    Thanks
    Esh

  91. Krish76 on February 4th, 2009

    Stefan
    I have a problem while remove the table rows.
    I have put in the correct javascript for adding an additional row and for deleting the row. When I click add button, it increases the number of row one below the other, but, when I click the remove button, it does not remove the final row in the table. Instead it removes some other row in the middle.
    I am using Livecycle designer 8.0. Could you please provide a solution.
    Krish

  92. Stefan Cameron on February 5th, 2009

    Eshwari,

    I think you’re always getting an extra row at the end because that’s the last thing you do before looping in your FOR loop. To keep it as it is, you should first check to see if (i + 1 < obj.length). If that's true, then add another row since you'll be looping. Otherwise, (i < obj.length) will be false and you'll exit the loop...

  93. Stefan Cameron on February 6th, 2009

    Krish76,

    If you always want to remove the last row, you’ll have to figure-out the number of rows, then pass the correct parameter to the removeInstance() method:

    if (_TableRow.count > 0)
        removeInstance(_TableRow.count - 1);

    where '_TableRow' is the instance manager of the table's repeatable row.

  94. Hilary on February 9th, 2009

    Stefan,

    I try again the app.setTimeout method you suggest and seems that the numpages did updated under xfa.host.print:

    However if I need to run certain script that follow the print action (say, myclosePreview(), I put it to follow xfa.host.print within app.setTimeout),
    ie.
    app.setTimeOut(‘xfa.host.print(1, “0”, (xfa.host.numPages -1).toString(), 0, 0, 0, 0, 0);xfa.form.form1.myclosePreview();’, 500);

    the script inside myclosePreview() cannot be run properly, some strange look happens to the form.

    If I don’t include myclosePreview() under timeout,
    i.e
    app.setTimeOut(‘xfa.host.print(1, “0”, (xfa.host.numPages -1).toString(), 0, 0, 0, 0, 0);’, 500);
    myclosePreview();

    myclosePreview() will get run BEFORE xfa.host.print under Timeout such that it cannot print out the rendered pages.

    Also, I am wondering if I can perform below actions if I need to import data for printing:
    1) Fetch a record
    2) Rendering that record to unknown rendered pages
    3) Print the rendered look
    4) Reset the form
    5) Get next record and loop again

  95. Stefan Cameron on February 13th, 2009

    Hilary,

    It sounds like there’s a timing issue with xfa.host.print in that it doesn’t occur immediately when you call it. So when you call this

    app.setTimeOut(’xfa.host.print(1, “0?, (xfa.host.numPages -1).toString(), 0, 0, 0, 0, 0);xfa.form.form1.myclosePreview();’, 500);

    ‘xfa.form.form1.myclosePreview();’ gets executed before ‘xfa.host.print()’ does. Maybe that’s the “strange look” you get?

    As for looping through records and printing the form, as long as you aren’t trying to circumvent the print dialog where the user must click the “print” button in order for the form to actually print, then I don’t see why you couldn’t do it.

  96. Prabhakaran T K on March 23rd, 2009

    Hi,

    I have a field in Master Page, and another in Body Page. In the initialize event of a Subform (in Body Page), I have to check the values of those above mentioned fields. I am able to get the value from field in Body PAge , and not from the field in Master Page. (i.e.) not able to access any object in Master page, from body page, in initialize event.

    The code which I use:
    var LoanPeriod = LOAN_APP_BODY_PAGE_1.FINANCE_REQ_FORM.PERIOD_LOAN.rawValue;
    var FormDate = xfa.form.vnPDFDataSource.pageSet.Page1.LOAN_HEADER.FORM_DATE.rawValue;

    I am getting the ‘LaonPeriod’ but not the ‘FormDate’.

    Please give me a solution.

  97. Stefan Cameron on March 28th, 2009

    Prabhakaran T K,

    The problem with your script is the event in which you’re attempting to execute it. Master Pages are directly related to the form’s layout and therefore they don’t exist until the form has been rendered.

    You have two options: (1) Put your code in the Layout:Ready event (however note that this event will be triggered every time the form’s layout changes, not just once on start-up), or (2) add hidden field to the body page, get/set the value there, set its name to be identical to the name of the field on the master page and then set its “Object palette > Binding tab > Default Binding property” to “Global” (if you’re binding it to a node in a data connection of some kind, then set the same binding on both fields instead of setting them to “Global” binding). The effect of (2) is that any value you put in the hidden field on the body page will be automatically reflected in the field on the master page. This way, you can keep your code in the Initialize event.

  98. Hilary on March 31st, 2009

    Stefan,

    Thanks for your response again. Just busy for something else but it’s great to see your words when I come back here.

    Well that means I couldn’t do it if I really need to avoid the print dialog for user to skip the “Confirm” each time, right? doesn’t matter maybe I’ll try another way then.

    Another question that I scratch my head to bald. I recently created a form to read each row from Excel and then generate rendered page(s) for each row (record) such that they are listed continuously. I do that by adding subform instance for each record.

    The subform(s) are flowed so that each record may have varying pages, depending on the content. I used to set the page information just at the bottom of the master page by adding “page n of m” control from the Custom object library such that when:
    record 1 has 2 pages
    record 2 has 3 pages
    record 3 has 1 pages

    may show following on the page footer:
    record 1 : “page 1 of 6, page 2 of 6”
    record 2 : “page 3 of 6, page 4 of 6, page 5 of 6”
    record 3 : “page 6 of 6”

    But actually I would like to show:
    record 1 : “page 1 of 2, page 2 of 2”
    record 2 : “page 1 of 3, page 2 of 3, page 3 of 3”
    record 3 : “page 1 of 1”

    It seems that it is impossible to use master page to display the page information but I can’t either set in the page as the subforms are flowed.

    Any idea?

  99. Stefan Cameron on April 7th, 2009

    Hilary,

    Here’s what comes to mind:

    First, I think master pages must be used otherwise you won’t get the page number always at the bottom of the page.

    Second, instances of fields on master pages are created when the master page instance is laid-out during a render. I believe this render happens once the form knows how many pages will be needed. In the master page field’s Layout:Ready event (which will occur once the form has been rendered), you could call a script object function, passing a reference to the field (‘this’) in order to get back the “page x of y” string. That script object function could first determine what physical page the field is sitting on (by using ‘xfa.layout.page(objectRef)’). Then it would search the flowed body section where the record instances appear, figuring-out where one set ends and the next begins. Finally, it would resolve the master page field’s physical page into a subset of pages pertaining to a set of records, effectively returning “page 2 of 3” for the second “page” of record #2.

    Does that make sense to you?

  100. Hilary on April 28th, 2009

    Stefan,

    Hope I understand what you inspired me.

    I have done some test and find out all pages’ Layout:Ready event will be triggered before all masterpages’ Lauout:Ready event. ie. all pages get rendered first before coming to all masterpages.

    So I tried to store up all physical page numbers of the start pages for all record instances at the page’s Layout:Ready event first. Then when it comes to masterpages, I may work out the desired “page x of y” by looking up the stored data.

  101. Sandeep on August 6th, 2009

    Hello Stefan,

    I have a form with 5 paragraphs. I wrapped each paragraph in a subform(sf1, sf2, sf3, sf4), also wrapped all these subforms in a flowed subform. Now, I want to hide and unhide these paragraphs based on a condition. This condition is sent through a payload XML.

    Let us say, i have a sequence
    1,2,3 sent in the payload XML to render the form. Based on this condition, I have to display first three paragraphs and I never had a problem to display this sequence.

    Now if i have a sequence,
    3,1,4.
    How can i change the sequence of the paragraphs in a flowed subform?
    All i know is, subforms in a flowed container are displayed in the order they are built in the hierarchy.

    Can you please let me know whether my approach is correct or not? Also, can you give me a solution to change the sequence of the subforms in a flowed container.

    Thanks in advance.

  102. Stefan Cameron on August 14th, 2009

    Sandeep,

    Since the moveInstance() method is only good for re-ordering instances of a single subform, it won’t help you in this case (because you need to re-order different subforms within a set).

    What you need to do is wrap those subforms into a Data-Nominated Subform Set (using the Hierarchy palette to wrap the subforms and choosing the “Object palette > Subform Set tab > Select one Subform from Alternatives type” — this replaces the subform that’s currently wrapping the paragraph subforms), put the subformSet into a flowed container, set the subformSet to repeat, tailor your XML so that it has the correct structure to make this work and set the binding expressions by clicking on the “Object palette > Subform Set tab > Edit Alternatives button”.

    For example, if your data looked like this:

    <data>
        <selector>
            <paraId>3<paraId>
        <selector>
        <selector>
            <paraId>1<paraId>
        <selector>
        <selector>
            <paraId>4<paraId>
        <selector>
    </data>

    The <selector> element repeats and each instance has a <paraId> element within it to which a subform within the subformSet can bind.

    Your binding expressions would then look like this:

    Name: sf1
    Binding: $record.selector
    Language: JavaScript
    Expression: paraId.value == "1"
    [...]
    Name: sf5
    Binding: $record.selector
    Language: JavaScript
    Expression: paraId.value == "5"

    The result is that the subformSet repeats for each <selector> node it finds in the data and, for each instance, chooses the subform that matches the expression. The result is the appearance that the subforms (paragraphs) have been re-ordered.

  103. Sandeep on August 17th, 2009

    Thank you Stefan.

    I really appreciate your patience in solving others problems.

  104. Randy on November 3rd, 2009

    Hello Stefan,

    I have come into a problem I have been trying to figure out for the past week. I had googled for some guidance yet I found nothing useful.

    Currently, I have a table which has an addinstance button and removeinstance button. The script for these work.

    What I am looking to do is the row starts off being in readOnly format and when I select a checkbox, one of the cell will become open ( I have a table with 12 cell in a row and about 6-7 checkboxes which will open certain cells). However, when I add an instance and one of the checkboxes is checked…the next row is still readOnly. How do I target those certain cells? if I have a checkbox checked and I add a new row..those certain fields will be open for all the cell in that column.

    If you could assist me, that would be great.

    Thanks,
    Randy

  105. Stefan Cameron on November 8th, 2009

    Randy,

    Assuming the cells enabled by the checkboxes are disabled by default and that the cells enabled by the checkboxes are in the same row as the checkboxes themselves, the script that enables a related cell when a checkbox is checked shouldn’t be trying to access a particular row instance. For example, if a checkbox enables a cell named CellA, its Click event would have script that checks for its rawValue and, if equal to “1”, would simply do “CellA.access = ‘open’;”. Scoping rules would result in the CellA field pertaining to the same row in which the checkbox resides.

  106. Tim on January 25th, 2010

    Hi Stefan, great blog. I have been working on a (dynamic)compliance inspection form. Using your examples, I have been able to create this form using a repeatable subform with a “addInstance” button and a ‘removeInstance” button that adds a deficiency subform for each deficiency found at the site. My issue is that once I get to the end of first page, I need the subform to flow to the top of page2, and so on. I have been able to control the number of instances on the first page to keep it from running into my page footer,(again, using your examples) but have been unable to get the subform to flow to the next page. I have 2 master pages created-page1 with site info and page2 as a continuence of page1. I know I am probably overlooking something and your help would be greatly appreciated.
    By the way, the form is setup the way you recommended for dynamic forms (dynamic pdf, flowed containers, etc.).

  107. Ralph on January 31st, 2010

    Hello Stef,

    I’m making a form that allows users to write instructions for maintenance routines.

    The form has an expandable table. Each row has an “Add”, “Delete”, “Up” and “Down”. When the user presses the “Add” button, a new row can be added. The “Delete” button removes the row where the button is location. The “Up” and “Down” moves each line on step depending on the direction. I was able to program these buttons thanks to you! Your blog saved me many hours of frustration and headache. Thank You!!

    However, I do have another problem. I also added a drop list that will automatically populate the table with default values depending on the choice. I used the setInstance function to change the number of rows.

    Suppose the user added so many lines that the table expands to 2 pages (30 rows). Then, the user picks a value in the dropdown list that reduces the number of table rows to 12, which results in just a 1-page form. Adobe Reader proceeds to alternate between a “Bad Parameter” error and a “Software Failure” error. I’m not sure what’s happening.

  108. Stefan Cameron on February 3rd, 2010

    Tim,

    Since you point-out that your form is setup using flowed containers, I’m going to assume the issue isn’t about the subform instance being “cut-off” at the bottom of Page1 but rather that it flows into another instance of the Page1 master page rather than flowing into a Page2 master page.

    In this case, you need to specify where new instances of the repeatable deficiency subform should be placed once Page1 overflows. You can do this by selecting the deficiency subform and setting its “Object palette > Pagination tab > After property” to “Go To Page > Page2”.

  109. Tim on February 8th, 2010

    Thank you for your response. As instructed, I selected the “Go To Page” option with some undesired results. I played with some of the other settings on the SF object pallete without success. There may be some things in the code causing conflict, I copied and pasted most of it and changed things to suite my form. It may be a little overkill, but I’m learning. See if I am on the right track.

    dataroot.MainPage.SF1.SiteObSF.Dk-SpFld::initialize – (JavaScript, client)
    /* This listbox object will populate two columns with data from a data connection.

    sDataConnectionName – name of the data connection to get the data from. Note the data connection will appear in the Data View.
    sColHiddenValue – this is the hidden value column of the listbox. Specify the table column name used for populating.
    sColDisplayText – this is the display text column of the listbox. Specify the table column name used for populating.

    These variables must be assigned for this script to run correctly. Replace with the correct value.
    */

    var sDataConnectionName = “< value &lt”; // example – var sDataConnectionName = “MyDataConnection”;
    var sColHiddenValue = “< value <“; // example – var sColHiddenValue = “MyIndexValue”;
    var sColDisplayText = “< value <“; // example – var sColDisplayText = “MyDescription”

    // Search for sourceSet node which matchs the DataConnection name
    var nIndex = 0;
    while(xfa.sourceSet.nodes.item(nIndex).name != sDataConnectionName)
    {
    nIndex++;
    }

    var oDB = xfa.sourceSet.nodes.item(nIndex).clone(1);
    oDB.open();
    oDB.first();

    // Search node with the class name “command”
    var nDBIndex = 0;
    while(oDB.nodes.item(nDBIndex).className != “command”)
    {
    nDBIndex++;
    }

    // Backup the original settings before assigning BOF and EOF to stay
    var sBOFBackup = oDB.nodes.item(nDBIndex).query.recordSet.getAttribute(“bofAction”);
    var sEOFBackup = oDB.nodes.item(nDBIndex).query.recordSet.getAttribute(“eofAction”);

    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(“stayBOF”, “bofAction”);
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(“stayEOF”, “eofAction”);

    // Clear the list
    this.clearItems();

    // Search for the record node with the matching Data Connection name
    nIndex = 0;
    while(xfa.record.nodes.item(nIndex).name != sDataConnectionName)
    {
    nIndex++;
    }
    var oRecord = xfa.record.nodes.item(nIndex);

    // Find the value node
    var oValueNode = null;
    var oTextNode = null;
    for(var nColIndex = 0; nColIndex < oRecord.nodes.length; nColIndex++)
    {
    if(oRecord.nodes.item(nColIndex).name == sColHiddenValue)
    {
    oValueNode = oRecord.nodes.item(nColIndex);
    }
    if(oRecord.nodes.item(nColIndex).name == sColDisplayText)
    {
    oTextNode = oRecord.nodes.item(nColIndex);
    }
    }

    while(!oDB.isEOF())
    {
    this.addItem(oTextNode.value, oValueNode.value);
    oDB.next();
    }

    // Restore the original settings
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(sBOFBackup, “bofAction”);
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(sEOFBackup, “eofAction”);

    // Close connection
    oDB.close();
    dataroot.MainPage.SF1.SiteObSF.CatFld::initialize – (JavaScript, client)
    /* This dropdown list object will populate two columns with data from a data connection.

    sDataConnectionName – name of the data connection to get the data from. Note the data connection will appear in the Data View.
    sColHiddenValue – this is the hidden value column of the dropdown. Specify the table column name used for populating.
    sColDisplayText – this is the display text column of the dropdown. Specify the table column name used for populating.

    These variables must be assigned for this script to run correctly. Replace < value < with the correct value.
    */

    var sDataConnectionName = “< value <“; // example – var sDataConnectionName = “MyDataConnection”;
    var sColHiddenValue = “< value <“; // example – var sColHiddenValue = “MyIndexValue”;
    var sColDisplayText = “< value <“; // example – var sColDisplayText = “MyDescription”

    // Search for sourceSet node which matchs the DataConnection name
    var nIndex = 0;
    while(xfa.sourceSet.nodes.item(nIndex).name != sDataConnectionName)
    {
    nIndex++;
    }

    var oDB = xfa.sourceSet.nodes.item(nIndex).clone(1);
    oDB.open();
    oDB.first();

    // Search node with the class name “command”
    var nDBIndex = 0;
    while(oDB.nodes.item(nDBIndex).className != “command”)
    {
    nDBIndex++;
    }

    // Backup the original settings before assigning BOF and EOF to stay
    var sBOFBackup = oDB.nodes.item(nDBIndex).query.recordSet.getAttribute(“bofAction”);
    var sEOFBackup = oDB.nodes.item(nDBIndex).query.recordSet.getAttribute(“eofAction”);

    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(“stayBOF”, “bofAction”);
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(“stayEOF”, “eofAction”);

    // Clear the list
    this.clearItems();

    // Search for the record node with the matching Data Connection name
    nIndex = 0;
    while(xfa.record.nodes.item(nIndex).name != sDataConnectionName)
    {
    nIndex++;
    }
    var oRecord = xfa.record.nodes.item(nIndex);

    // Find the value node
    var oValueNode = null;
    var oTextNode = null;
    for(var nColIndex = 0; nColIndex < oRecord.nodes.length; nColIndex++)
    {
    if(oRecord.nodes.item(nColIndex).name == sColHiddenValue)
    {
    oValueNode = oRecord.nodes.item(nColIndex);
    }
    if(oRecord.nodes.item(nColIndex).name == sColDisplayText)
    {
    oTextNode = oRecord.nodes.item(nColIndex);
    }
    }

    while(!oDB.isEOF())
    {
    this.addItem(oTextNode.value, oValueNode.value);
    oDB.next();
    }

    // Restore the original settings
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(sBOFBackup, “bofAction”);
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(sEOFBackup, “eofAction”);

    // Close connection
    oDB.close();
    dataroot.MainPage.SF1.SiteObSF.ObDesFld::initialize – (JavaScript, client)
    /* This listbox object will populate two columns with data from a data connection.

    sDataConnectionName – name of the data connection to get the data from. Note the data connection will appear in the Data View.
    sColHiddenValue – this is the hidden value column of the listbox. Specify the table column name used for populating.
    sColDisplayText – this is the display text column of the listbox. Specify the table column name used for populating.

    These variables must be assigned for this script to run correctly. Replace < value < with the correct value.
    */

    var sDataConnectionName = “< value <“; // example – var sDataConnectionName = “MyDataConnection”;
    var sColHiddenValue = “< value <“; // example – var sColHiddenValue = “MyIndexValue”;
    var sColDisplayText = “< value <“; // example – var sColDisplayText = “MyDescription”

    // Search for sourceSet node which matchs the DataConnection name
    var nIndex = 0;
    while(xfa.sourceSet.nodes.item(nIndex).name != sDataConnectionName)
    {
    nIndex++;
    }

    var oDB = xfa.sourceSet.nodes.item(nIndex).clone(1);
    oDB.open();
    oDB.first();

    // Search node with the class name “command”
    var nDBIndex = 0;
    while(oDB.nodes.item(nDBIndex).className != “command”)
    {
    nDBIndex++;
    }

    // Backup the original settings before assigning BOF and EOF to stay
    var sBOFBackup = oDB.nodes.item(nDBIndex).query.recordSet.getAttribute(“bofAction”);
    var sEOFBackup = oDB.nodes.item(nDBIndex).query.recordSet.getAttribute(“eofAction”);

    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(“stayBOF”, “bofAction”);
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(“stayEOF”, “eofAction”);

    // Clear the list
    this.clearItems();

    // Search for the record node with the matching Data Connection name
    nIndex = 0;
    while(xfa.record.nodes.item(nIndex).name != sDataConnectionName)
    {
    nIndex++;
    }
    var oRecord = xfa.record.nodes.item(nIndex);

    // Find the value node
    var oValueNode = null;
    var oTextNode = null;
    for(var nColIndex = 0; nColIndex < oRecord.nodes.length; nColIndex++)
    {
    if(oRecord.nodes.item(nColIndex).name == sColHiddenValue)
    {
    oValueNode = oRecord.nodes.item(nColIndex);
    }
    if(oRecord.nodes.item(nColIndex).name == sColDisplayText)
    {
    oTextNode = oRecord.nodes.item(nColIndex);
    }
    }

    while(!oDB.isEOF())
    {
    this.addItem(oTextNode.value, oValueNode.value);
    oDB.next();
    }

    // Restore the original settings
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(sBOFBackup, "bofAction");
    oDB.nodes.item(nDBIndex).query.recordSet.setAttribute(sEOFBackup, "eofAction");

    // Close connection
    oDB.close();
    dataroot.MainPage.SF1.SiteObSF.AddSubform::click – (JavaScript, client)
    // nSubLength stores the number of XML elements contained in SF1.
    var nSubLength = SF1.nodes.length;

    // nNumSub is used to calculate the number of subform objects contained in SF1.
    var nNumSub = 0;

    // This script uses a For loop to cycle through all of the objects contained
    // in SF1.
    for (var nCount = 0; nCount < nSubLength; nCount ++) {

    // If the current XML element in Subform1 is of type subform, then increment
    // the variable nNumSub by one.
    if (SiteObSF.nodes.item(nCount).className == "SiteObSF") {
    nNumSub = nNumSub + 1;
    }
    }

    // An if-else statement is used to prevent form authors from adding more than the
    // maximum number of four subform instances to a form.
    if (nNumSub == 4) {
    xfa.host.messageBox("Click Add Page for more Descrepencies", "Attention", 3);
    }
    else {

    // This script uses the addInstance() method to add a new instance of the
    // SiteObSF object contained in SF1.
    SF1.SiteObSF.instanceManager.addInstance(1);
    }
    xfa.form.recalculate(true)

    dataroot.MainPage.SF1.SiteObSF.RemoveSubform::click – (JavaScript, client)
    var nNumSub = 0;
    var nSubLength = SF1.nodes.length;
    SF1.SiteObSF.instanceManager.removeInstance(nNumSub);

    I can email a copy of the form if you like.

    Tim

  110. Stefan Cameron on February 11th, 2010

    Ralph,

    Thanks for letting me know how much my tutorial helped you out!

    Adobe Reader proceeds to alternate between a “Bad Parameter” error and a “Software Failure” error. I’m not sure what’s happening.

    I don’t recall ever seeing such an error. Can you isolate the line of script that seems to cause it? Perhaps you should try setting the number of instances to 0, the setting it to what it should be? That might make a difference.

    Tables should be able to span pages provided you checked the “Allow page breaks within content” property on the “Object palette > Table tab”.

  111. Stefan Cameron on February 11th, 2010

    Tim,

    Perhaps you are running into the issue where your table is being “cut-off” at the bottom of your page (and might even be running over top your footer) instead of flowing onto the next page.

    If you have 2 master pages just so that you can have 2 pages, that’s not necessary. A master page is simply the “background” to all the body pages. You can get fancy and create different master pages with different setups like multi-columns, headers, footers, etc., and have body form objects flow between various master pages but that sounds way more complex than what you’re trying to accomplish.

    I would start by deleting the second master page.

    Then, make sure your table is allows to break between pages by checking the “Object palette > Table tab > Allow page breaks within content”.

    Finally, make sure your table is within a flowed container. This will ensure that anything below your table flows down nicely as you add rows.

    As for your footer, make sure the content area object on the master page doesn’t overlap your footer, otherwise body content will flow over top of it. The content area object on the master page defines where body content can flow within the page.

    The dynamic form portions of my MAX 2007 tutorial explain this in depth.

  112. Mike S on March 1st, 2010

    Stefan,

    Have you ever seen instanceManager not work at all. I started with a blank form and added a table just as the scripting refernce instructed and the button does nothing. I do not even get an error or something to point me in a direction.
    form is dynamic, page is flowable, table with one header row, and dynamic body rows – used the assistant to create, added the script Table1._Row1….. with and without instanceManager … .addInstance(1);

    nothing – the button clicks and that is it.

    I have an older form that adds rows and it works inside designer. I tried copying that over into another form but nothing.

    Any suggestions would be appreciated.

  113. Stefan Cameron on March 3rd, 2010

    Mike S,

    It sounds like you’re previewing as a Static (non-interactive) PDF rather than a Dynamic PDF.

    The Instance Manager will only work in Dynamic PDFs. See this post on how to ensure you’re previewing as a Dynamic PDF.

  114. Haylee on April 15th, 2010

    Stefan,

    It seems you have been able to help several people and since I”m about Googled out on trying to find a solution for my problem, I would try here.

    I need a table that can have static rows and then dynamically Add Rows in between the static rows.

    For example, a table with 3 columns and the structure would be the following (I hope this isn’t confusing):

    Header
    Row1 Col1 – A place holder for the rows Delete Button (this row doesn’t actually have a button though because they shouldn’t be able to delete it)
    Row1 Col2 – A static text field
    Row1 Col3 – A date/time field that the user can change
    Row 2 Col1 – A Delete Button to delete the Row
    Row 2 Col2 – An empty text field that the user can type in
    Row2 Col3 – A date/time field that the user can change
    Row3 Col1 – A place holder for the rows Delete Button (this row doesn’t actually have a button though because they shouldn’t be able to delete it)
    Row3 Col2 – A static text field
    Row3 Col3 – A date/time field that the user can change

    And then underneath I have an Add button to Add a Row. I want the rows added between Row 1 and Row 3. So basically add a duplicate of Row 2, after Row 2. I couldn’t figure out how to add the duplicate row of Row 2 instead of Row 1.

    I hope this makes sense. It’s a little hard to explain.
    Thanks in advance for your help.

  115. Stefan Cameron on April 25th, 2010

    @Haylee,

    This is certainly possible, but will require some script to make it work.

    Setup your table as you described it. Then select the Row2 row and make it repeatable by checking its “Object palette > Binding tab > Repeat Row For Each Data Item property”. Since all instances of a repeatable row or subform are always kept in sequence (“together”), this will ensure that new instances of Row2 appear after the last instance of Row2 and before Row3.

    Add the “Add Row” button beneath the table.

    Select the table and set its “Object palette > Table > Allow Page Breaks Within Content” property.

    Select the page subform (named “(untitled Subform) (page 1)” in the Hierarchy palette) and set its “Object palette > Subform tab > Content property” to “Flowed”. This, combined with the previous step, will ensure that the table isn’t cut-off at the bottom of the page (a new page will be generated instead) if you add enough rows to fill a page.

    At this point, the table, followed by the AddRow button, should be at the top left corner of your form. Now for the scripts.

    Select the AddRow button and use the Script Editor palette to set its Click event script (make sure you set the language to “JavaScript”):

    // JavaScript:
    // get the last instance of Row2 to copy values from
    var previousRow = Table1.resolveNode("Row2[" + (Table1._Row2.count - 1) + "]");
    
    // add a new Row2 instance
    var newRow = Table1._Row2.addInstance(0);
    
    // copy values from the previous instance
    newRow.TextField1.rawValue = previousRow.TextField1.rawValue;
    newRow.DateTimeField1.rawValue = previousRow.DateTimeField1.rawValue;
    

    The script above will add a new instance of Row2 and copy values from the last instance that was added prior to that. (Note that the names of the fields which represent the text field and date time fields in Row2 may be different for you depending on how you created your table so make sure you use the right names.)

    Now select the Delete button in Row2 and set its Click event script:

    // JavaScript:
    // delete this row only if there's more than one row left
    if (Table1._Row2.count > 1)
        Table1._Row2.removeInstance(this.parent.index);
    

    The script above will remove the instance of Row2 to which belongs the Delete button that was clicked.

  116. Devra Glowinski on June 19th, 2010

    Dear Stefan,

    I have read several times now on your site that to make a subform repeatable, it must be Flowed. And that statement does not correspond to what I am seeing in the forms I am working with. I am fairly new to LiveCycle, so would greatly appreciate clarification.

    I have inherited several forms that have continuation pages. These pages are full page subforms. We start out with a minimum of 1 continuation page, but make it hidden to start. The user then can “add” one continuation page, making the first continuation page visible. From that continuation page, the user can an additional pages, which creates and additional instance with the instanceManager.

    This is working very well. However, the properties of the continuation page is Positioned, not flowed. So why is this working?

    Another question: I have another form which has three pages for data entry and 12 subsequent pages which are basically text. We have buttons on the form to allow the user to delete any specific page, or a multiple group of pages (to save paper for printing purposes, I believe). The programmer before me accomplished this by making these “deleted” pages hidden, but there are a few problems with his code, and I thought I would fix the code by actually deleting the pages using instanceManager.removeInstance().

    I want all 15 pages to appear when the user first opens the form, which would mean I have a minimum count per page of 1. However, as I understand it from your tutorials, that would prevent me from deleting the pages. Can I set a minimum count of 0, and add the pages when the form is first created? If not, do you have any other suggestions?

    Thank you in advance for your help. Your site is awesome and I am so glad you are willing to share your knowledge and experience with us newbies.

    Devra

  117. Stefan Cameron on June 29th, 2010

    @Devra Glowinski,

    I’m glad you find my blog useful!

    Regarding flowed vs positioned subforms: To make a subform repeatable (such as the “continuation pages”), that subform must be inside a flowed subform. The “continuation page” subform itself doesn’t have to have flowed content (as you can see). Look at the subform that contains the “continuation page” subform: its content should be flowed.

    Regarding removing pages: You are correct in thinking that setting a minimum count would prevent instanceManager.removeInstance() from working properly. I would suggest setting the minimum to zero and the initial and max counts to 1. That would give you an instance of each page from the start and removeInstance() should work just fine after that.