Stefan Cameron on Forms
Building intelligent forms using Adobe LiveCycle Designer

Demystifying IM.AddInstance

The definition for the Instance Manager’s addInstance method is as follows:

addInstance( [BOOLEAN param] )

Personally, I find the word param a little vague. I think it should be called merge instead because that would give a clearer indication as to what it stands for: Essentially, if the data you’ve merged into your form has a repeating section that you’ve bound to a repeating (dynamic) subform and not all instances of this repeating data section have been merged into the form’s layout, you can control whether the new instance of the dynamic subform pertaining to that repeating data section will get merged with the next section or not by specifying true (merge — the default) or false (don’t merge — show empty instance) as the parameter value.

Usually, this doesn’t make a difference because you typically merge all instances of a repeating data section into your form and once there are no more data instances to merge, addInstance(true) will yield the same results as addInstance(false): an new empty (no data merged-in) instance.

Consider, however, the case where your form is a report and you’d like to show only a glimpse of the data instead of loading all 2000 instances of a repeating section. If your data was sorted from the most important to the least important, you could significantly improve the performance of your report (with respect to load time) by limiting the number of instances of that repeating data section that are initially merged into your form. Once your form is loaded, it could provide ways for the reader to get more instances if they wish to do so.

This can be achieved by using the Max property on the dynamic subform’s Binding tab in the Object palette along with addInstance(true).

Here’s a sample which has a data connection to an XML Data File that lists movies. There are 16 movies in total but the form limits the number of movies initially displayed to 10 and provides a Show More button. When this button is pressed, the movie dynamic subform’s Max count property, initially set to 10, is incremented by 1 and a new movie instance is added using true as the parameter.

Download Sample [zip]

Minimum Requirements: Designer 7.0, Acrobat 7.0.

Note: If you open the form in Acrobat, don’t forget to import the data into it using the “File | Form Data” menu.


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

36 Responses to “Demystifying IM.AddInstance”

  1. Sergio on August 1st, 2006

    Hello,

    I hope you could help me on this subject:

    In my pdf form I´m creating instances programatically using the setInstances() function.

    Each instance of the form has a input field which I would also like to assing programatically.

    However, despite I´m able to create the instances, I hadn´t figured out a way to assing this values.

    Do you think this is possible?

    Thank you

  2. Stefan Cameron on August 2nd, 2006

    Sergio,

    Unfortunately, the setInstances method doesn’t return a list of references to the new set of instances that remain after the call so, unlike addInstance which gives you a reference to work with, you have to go looking for them.

    Let’s say you’re creating instances of a subform named “sub” and each sub object has a field in it named “f”.

    First, you set the number of instances like this:

    _sub.setInstances(5);

    This statement creates 5 instances of the sub object.

    Now, you need to find the instances (get references to the m) so you can access the f object inside of them (in JavaScript here, within the context of the sub object’s parent subform):

    for (var i = 0; i < _sub.count; i++)
    {
      // get a reference to the sub[i] object
      var oSub = this.resolveNode("sub[" + i + "]");
      // set the value of its f object
      oSub.f.rawValue = "hello";
    }

    The loop simply obtains the number of instances using the count property of the sub object’s Instance Manager and, for each instance, gets a reference to it in order to set its f object’s value.

  3. Palani on August 28th, 2006

    Hi,

    We are able to convert from Jetform IFD to PDF using Adobe Designer 7.0 through manually(Open “Save As PDF”).The only problem is that I need to do this automatically, without manually opening Adobe Designer 7.0.

    We are able to convert Jetform IFD to XDP using ConvertIFD.exe through Command line parameter (ConvertIFD.exe in=C:\Sample.ifd” out=”C:\Sample.XDP”). Is there a way I can programmatically generate PDF from XDP?

  4. Stefan Cameron on August 31st, 2006

    Palani,

    I had to do a little investigating to see what the situation was with batch-converting XDPs to PDFs.

    Unfortunately, neither Designer nor Acrobat ships with a tool that would let you do this. While there are command line programs to convert IFD, Word and PDF documents into XDP, there isn’t one that’ll take an XDP and convert it into a PDF.

  5. Harish Kumar C on September 21st, 2006

    Hi Palani & Stefan Cameron,

    I am also facing the same challenge to convert XDP into PDF.
    Have you got any solution for this. If so, could you please send it to my id (harish_c_99@yahoo.com).

    Thanks a lot.

    -Harish

  6. Deb Mannon on November 16th, 2006

    I’ve grown with JetForm/Acellio/Output Designer/Adobe Designer since the mid-90′s but find myself in need of quickly converting a PDF or Word file to an IFD. I simply must find a quick and easy way without jeopardizing the integrety of the document. Is there a tool out there to accomplish this?

    Thanks for any help.
    ~Deb

  7. Stefan Cameron on November 19th, 2006

    Deb,

    That was a good question! I had to do some digging around to find the answer since those technologies pre-date my arrival at Adobe.

    Unfortunately, we don’t make a tool to convert IFDs into PDFs but you may be able to find one at texcel.com.

  8. Ian Simons on August 29th, 2007

    Hi Stef,

    Firstly a quick note to say thanks for running this blog, I started using LiveCycle about 3 days ago, and it’s totally invaluable to a novice.

    I had a specific problem to solve, mainly to do with dynamic forms (that alter the number of rows depending on a drop down list) but also are capable of changing the text in each row; I have now solved this problem, and wanted to post this here for others that might be trying to achieve this – hence it will be all in one place. I tried the little routine you gave Sergio up there, but could not get it working, although the point that if you use setInstances(x) it becomes tougher to address each new row (instance) made an impression, so I decided to use ‘addInstance’ to make it simpler for me :) .

    I started a new global array to reference the new rows, in order to avoid nested if-else statements. The variables NumOfRows (Integer), var1, var2 and var3 (text) are globally declared within the form properties. DropList is the value returned when a selection is made form the drop-down list. I used setInstances(0) to reset the dynamic table before iterating the if statement to stop the table growing if a wrong srop-down list selection is made.

    I think the main bit which makes the code a little clunky is there is no checking that there are the same number of elements in ‘NewArray’ as the integer in ‘NumOfRows’.

    *script*

    var NewArray = new Array(var1.value,var2.value,var3.value);

    if (DropList.rawValue == 1)
    {
    Table1._Row1.setInstances(0);

    for (var i = 0; i < NumOfRows.value; i ++)
    {
    var oNewText = Table1._Row1.addInstance(1);
    oNewText.TextField1.rawValue = NewArray[i];
    }

    }

    This now works exactly how I want it to – I hope others find it useful!

    Do you have any tips on how I could further improve the code?

    Ian

  9. Ian Simons on August 29th, 2007

    Dang. Typo’s! You get my drift. :)

  10. Stefan Cameron on September 2nd, 2007

    Ian Simons,

    I don’t have anything to add. Your script seems just fine for resetting a table’s rows and then adding a set number of rows (based on the “NumOfRows” form variable) each with pre-determined text (based on the “varX” form variables). I’m glad to hear that you were able to get to a solution that works for you.

    Thanks for sharing!

  11. KX on February 21st, 2008

    Stefan,
    First off, great blog. I was able to get a lot of information from your samples and tutorials. Still in the process of going through the examples, but definitely a site I will bookmark and share with other colleagues.

    I downloaded this particular tutorial, and noticed that setting movie subform’s initial count seems to have no effect on how many rows get displayed on the form initially. For example, if I set the initial count to 2, minimum to 1, maximum to 10, I’d expect to see only 2 rows when I preview the form. However, I still see 10 rows. I am using LiveCycle Designer ES 8.1 Thoughts?

    Thanks!

  12. Stefan Cameron on February 22nd, 2008

    KX,

    Thanks for the feedback and encouragement!

    Actually, the initial property is behaving properly in this case. A subform’s initial property is only meant for the case where there’s no data (or not enough) being merged into the form but doesn’t impose any limits on the amount of data that gets merged into the form. The min and max properties are the actual hard limits.

    Since the movieData.xml file contains 16 records, the merging algorithm will attempt to merge-in all 16 however the hard-stop is the subform’s max property which limits this to 10 records.

    If you set the initial count to 2, min to 1 and max to 10 and merge-in the movieData.xml file, then you would still end-up with 10 rows filled with movie data (the 10 first ones). If you previewed the form without any data, then you would see 2 empty rows (because of the initial count). If you set initial to 0 but kept min set to 1 and previewed without data, you would see 1 empty row.

  13. KX on February 25th, 2008

    Stefan,
    Thanks for the reply. What you explained makes sense.

    Another question. We are considering converting some old Output Designer forms to LiveCycle ES forms. A lot of times, a form layout is dependent on the data to be merged (we’d be using XML data). There are simple cases to just hide or show a field based on data, or complicated cases like rendering only part of repeating data, continuing on to render other pages, then rendering the rest of the repeating data as appendix (if applicable). You sort of touched on that subject in another post, suggesting on using pagearea.

    I am interested to know more about how to manipulate a form layout based on the data to be merged. For instance, in this example, can you get the number of movies in the xml data file? Instead of a “Show More” button that shows one more at a time, use a “Show All’ button that shows all the records at once? (In this case, it’ll show all 16 rows).

    I guess I am asking how to get a reference to the Data DOM, and do interesting things from there. Any examples would be greatly appreciated.

    Thanks!

  14. KX on February 27th, 2008

    Stephan,
    I noticed that a couple of readers have asked about how to merge part of repeating data in a form, display some other content or pages, then display the rest of the repeating data. This is actually pretty common in real-life forms. I experimented with it a little bit using the form you provided in this example.

    I defined two subforms movie1, and movie2. Both binding to $record.movie[*] and are set to repeat subform for each data item. Movie1 subform also has max set to 3, while movie2 subform has no such restriction. I have movie1 subform on page 1, followed by some additional pages and then movie2 subform on last page.

    movie1 number field has the same script you provided:
    movieData.MoviePage1.movie1.number::calculate – (JavaScript, client)

    var nNumMovies = 16;

    if (this.parent.index

  15. Stefan Cameron on February 28th, 2008

    KX,

    In response to your question about accessing the Data DOM directly, you can do that in JavaScript by using

    xfa.datasets.data

    Once data has been merged into the form, the Data DOM is populated with properties that represent the various nodes in the XML data. This means that you could get the movie count like this:

    var movies = xfa.datasets.data.movieData.resolveNodes("movie[*]");
    var count = movies.length;

    (Notice it’s “resolveNodes” here, with an “s”, since the expression resolves to a list of nodes.) You could also access the properties of the first movie like this:

    var firstMovie = xfa.datasets.data.movieData.resolveNode("movie[0]");
    var movieTitle = firstMovie.title.value;

    (Notice it’s “resolveNode” here, without the “s”, since the expression resolves to a single node. Also note that in JavaScript, you must access the node’s “value” property in order to get its content.)

  16. Stefan Cameron on February 28th, 2008

    KX,

    Unfortunately, your second question got cut off, probably at the point in your script where you had a < bracket. You must use the HTML character code “&lt;” for those otherwise it’s interpreted as HTML code…

    In any case, I don’t believe there’s an easy way to do what you’re asking for simply because of the greedy nature of the merging algorithm (the second it matches the “$record.movie[*]” binding on the movie1 subform, it will merge all movie data into that subform and only that subform).

    You would have to do the merging manually by looking at the Data DOM, as I explained in my previous response, and then using the Instance Manager to create and populate instances of the movie1 and movie2 subforms. Not easy, but possible.

  17. KX on February 28th, 2008

    Stefan,
    Thanks for your reply. Sorry about the cut-off reply.
    Regarding my second question, I thought there would be no easy way to do that too. That’s why I thought what was interesting about the following behavior. Let me try to post it again.

    I defined two subforms movie1 on first page, some additional pages, and movie2 subform on the last page. Both subforms binding to $record.movie[*] and are set to repeat subform for each data item. Movie1 subform also has max set to 3, while movie2 subform has no such restriction. I have movie1 subform on page 1, followed by some additional pages and then movie2 subform on last page.

    movie1 number field has the same script you provided:
    ———————————————————–
    movieData.MoviePage1.movie1.number::calculate – (JavaScript, client)

    var nNumMovies = 16;

    if (this.parent.index < nNumMovies)
    this.rawValue = (this.parent.index + 1) + “/” + nNumMovies + “.”;
    else
    this.rawValue = (this.parent.index + 1) + “.”;
    ——————————————————————–

    movie2 number filed has slightly different script:
    ———————————————————–
    movieData.MoviePage2.movie2.number::calculate – (JavaScript, client)

    var movie1Count = movieData.MoviePage1.movie1.instanceManager.count;

    if ((this.parent.index+ movie1Count) < nNumMovies)
    this.rawValue = (this.parent.index + movie1Count + 1) + “/” + nNumMovies + “.”;
    else
    this.rawValue = (this.parent.index + movie1Count + 1) + “.”;

    ——————————————————————–
    What was surprising to me was that previewing the document actually shows first 3 movie records in movie1 subform on first page, followed by additional pages, then movie records 4 to 16 in movie2 subform on last page. I didn’t expect this, as you said, given the greed nature of merging algorithm, I thought I’d have to write some script in movie2 subform to removeInstance for the first 3 records in order to show only records starting from 4 to 16. But that’s not the case. It seems like the merging process has a pointer to where it left off in the movie1 subform (which is after the third movie record), and then when merging movie2 subform, it picks up where it left off, from the 4th movie record.

    Not sure if I explained clearly. I can email you the form if you’d like to see that. Anyway, my question was that was that the intended behavior when you have multiple bindings to the same source? Is this kind of behavior something you can use in designing forms?

    Thanks!

  18. KX on February 28th, 2008

    Stephan,
    Thanks for the script to access DataDOM. It’s exactly what I was looking for.

  19. Stefan Cameron on March 7th, 2008

    KX,

    Well I tried what you indicated and sure enough, it works! I’ve inquired about this to those that know more about the merging algorithm so that they can tell me if this is expected or not but they haven’t gotten back to me yet so I’ll post another comment when they do.

    I suppose I could justify the behaviour by reasoning that since it is correct to have the same binding on multiple form objects, the limit of 3 on the movie1 subform causes the merging algorithm to look for another place to keep merging-in data and movie2 satisfies that search. If movie2 didn’t exist, then it would stop at the 3rd movie in movie1 until you increased the maximum and added new instances using addInstance(1). But those are simply my musings and aren’t necessarily the way things really work so this shouldn’t be considered a formal answer.

  20. Stefan Cameron on March 12th, 2008

    KX,

    My colleague just confirmed that this is, in fact, expected behaviour so you should be able to expect this to keep on working in subsequent releases of LiveCycle.

  21. KX on March 17th, 2008

    Thank you very much, Stefan!

  22. Dani on April 23rd, 2008

    Hello everybody

    I’m trying to use your example in my own PDF but I have a bit problem. I add a new subform but I can’t see it. My PDF structure is like this (maybe translation would be wrong, i’m using spanish version):
    Form
    -(page format)
    –page1
    —(Area content without title)
    -(subform without title)
    –subformulario
    —field1
    —field2
    —field3
    -button
    -(objects referenced)

    I have a script in button, like this:
    xfa.host.messageBox(“Nº de campos”+xfa.record.nodes.length);
    subformulario.occur.max += 1;
    _subformulario.addInstance(true);
    xfa.host.messageBox(“Nº de campos”+xfa.record.nodes.length);

    and when executed this action, I have two messages. First “Nº de campos 1″ and then “Nº de campos 2″ so I suppose the new subform is added but, I don’t know why, I can’t see it on my PDF. Any suggestions? Thanks in advance.

  23. Stefan Cameron on May 1st, 2008

    Dani,

    I’m not sure what you’re trying to do with “xfa.record.nodes.length”. It looks like you’re trying to determine the number of instances of the subform that exist. You should be doing that with _subformulario.count instead.

    Have you made “subformulario” repeatable using the “Object palette > Binding tab > Repeat checkbox”?

    Have you checked the JavaScript console in Acrobat to see if there are any scripting errors? You can access this console by clicking on the PDF in the Preview tab and pressing “Ctrl + J”.

  24. Dani on May 6th, 2008

    Hi Stefan

    At first, thanks for your answer. I have resolved my problem. The error was my form wasn’t saved as a dynamic form so I couldn’t see the new subforms added. But now I have another problem.

    If I have a subform called “subformulario” with 3 fields and I add a new subform, this new subform will be name “subformulario”, isn’t? so…how can I get a value of this new subform? I have this new code but it doesn’t work ok

    #subformulario[1].contador.rawValue = subformulario.instanceManager.count;

    and i haven’t found any solution about how to resolved it. Any idea about this error?

    Thanks again.

  25. Dani on May 6th, 2008

    Sorry, the code was wrong. This is the right code

    xfa.form.Form.#subformulario[1].contador.rawValue = subformulario.instanceManager.count;

    And this is the error in javascript console

    SyntaxError: illegal character
    6:XFA:movieData[0]:Botón1[0]:click

  26. Stefan Cameron on May 14th, 2008

    Dani,

    When you use the addInstance(0) method of the Instance Manager, you get a reference to the new object as a return value. Therefore, you would do this:

    var newSubform = _subformulario.addInstance(0);
    newSubform.contador.rawValue = _subformulario.count;

    If the instance already existed and you wanted to access it (say the 2nd instance of subformulario) using JavaScript, you would have to use the resolveNode method:

    var secondInstance = parentOfSubformulario.resolveNode("subformulario[1]");
    secondInstance...

    As for the error you’re getting, I suspect it’s due to the fact that you’re using “#” in an expression with JavaScript set as the script language. “#” is not a legal JavaScript token.

  27. Chris on May 17th, 2008

    Stefan,

    I am seeing a strage behavior in my own form when using addInstance().

    I am using a data connection to prefill a form. The form has a Add_Row button that executes the following two lines as the click event:

    root.PO.grid.Table1.#subformSet[0].action.Button4::click: – (JavaScript, client) –
    Table1._r2.addInstance(1);
    xfa.form.recalculate(1);

    Some fields in the row “r2″ have default bindings such as $record.item[*].OrderMult and $record.item[*].ItemDesc1, etc. which are used during pre-population.

    The strange behavior is that all of the ItemDesc1 fields of the rows created with the dynamic form Button4 have the same value regardless of which row I enter a value. This “linked”-value behavior happens for all of the row fields that have a Non-Normal default binding.

    Do I need to explictly change the bindings of the newly added instances to prevent this problem? How would that be done?

  28. Stefan Cameron on May 26th, 2008

    Chris,

    I’m having some difficulty understanding the structure of your form but bindings that start “$record” are anchored to the root data node rather than being relative to the parent’s binding. So you could try to bind the “r2″ table row to “$record.item[*]” and then bind the fields inside to “OrderMult” and “ItemDesc1″ (no “$record.item[*]” prefix) which would make those field’s bindings relative to the instance of the <item> data node that gets bound to the instance of the “r2″ row.

  29. Dani on June 9th, 2008

    Hi Stefan

    I haven’t could find any suggestion about my problem so i hope you can help me. I’m trying to convert an excel file to pdf dinamic form using livecycle but, when I try to add a subform or a table on it, these options are disabled showing this message “this type of object can not be used in PDF forms with illustrations” (i had to translate this message because I’m using spanish version, so it can not to be right) I have tried to find a solution but I haven’t found. Any suggestion about this problem?

    Thanks for all your help

    Dani

  30. Stefan Cameron on June 14th, 2008

    Dani,

    I think what has happened is that you imported your Excel worksheet with the “fixed pages” (“artwork”) option. This creates a form with high-fidelity layout towards your original document however it prevents you from using objects that involve subforms (subforms themselves as well as tables). There are also other restrictions such as not being able to use text, rectangle, circle and line objects amongst others. The benefits of importing your form this way is that the original layout (from Excel) is preserved and you’re meant to overlay fields on it.

    You may also import a form with “flowable layout”. This will give you the ability to use subform-based objects however the import utility will translate every last piece of the original document into an XFA object. In certain circumstances, this can yield forms with 100s of objects.

    There’s a third option you may with to use, since you’re importing an Excel spreadsheet and that’s Designer’s ability to paste cells from an Excel spreadsheet. I believe this was introduced in Designer 8.0. Simply copy and paste cells from your spreadsheet right into Designer. You should get a special “paste from Excel” dialog when you paste into Designer that will guide you through the process. I think the end-result is a table object in your form.

  31. Amit on July 15th, 2008

    Hi Stefan,

    A very good blog, got to know many a new things.
    Despite of me being working with forms for a considerable time, this blog of urs gave me a lot new things to learn. Thanks..

    We are working on a scenario wherein we are add the instances of a subform depending upon the value entered by user at runtime.
    That subform has around 15 dropdowns, so i’ve used the addInstance method to add the instances of the subform at runtime.
    The value populated into the dropdowns is coming from some backend system, that is they are not static. So, wht is my problem is when i add the instances of that subform, they get added but the values of dropdown are there only in the first instance, rest all dropdowns in the rest of the instances are blank.

    Need your help..

  32. Stefan Cameron on July 20th, 2008

    Amit,

    It sounds like your form is configured to populate the drop downs in the first instance however not in the new instances. The addInstance() method actually returns a reference to the new instance which you can use to access the drop down lists and populate them as required:

    var newInstance = _repeatableSubform.addInstance(0);
    newInstance.DropDownList1.addItem("item 1");
    ...

    Another way to do it is to use the repeatable subform’s Initialize event (you can access it using the Script Editor palette, “Window menu > Script Editor”) to populate the drop down lists. When you add new instances, their Initialize events are triggered and they’ll automatically populate the drop down lists that pertain to them.

  33. Saran on September 20th, 2008

    hi i have the issue of resetting the form data. I could reset the datas typed inside the Table rows. But when i us the xfa.host.resetDate(); script it doesnt reset the rows and make just one row to be visible. User can then add the rows using a Button which adds rows.. Please help me

  34. Stefan Cameron on September 24th, 2008

    Saran,

    Are you giving the proper reference to the xfa.host.resetData() method? You should be giving it the SOM expression to your table. For example,

    xfa.host.resetData(myTable.somExpression);

    If that doesn’t work, you can always use the table row’s Instance Manager to remove all instances and add a blank one (assuming you’ve set the row to be repeatable and you’ve set a minimum instance count of 0 (zero) using the “Object palette > Binding tab”):

    myTable._Row1.setInstances(0); // remove all
    myTable._Row1.addInstance(0); // add a new blank row
  35. sha on July 15th, 2010

    Hi Stefan,

    I am using Adobe Output Designer 5.5, and few .ifd and .mdf files. The task is to convert these to .pdf files. ]
    I am able to covert the .ifd to .mdf but unable to convert .mdf to .pdf… Could you please let us know how to go further.

    Thanks
    Sha

  36. Stefan Cameron on July 22nd, 2010

    @sha,

    Adobe LiveCycle Designer can open (import) .ifd files since the very first version, I believe. Once imported, you will have an XFA form which you can save as a PDF file.