Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.
SOLVED

Dynamic Form is painfully slow to open and edit

Avatar

Level 4

I'm hoping someone could take a few minutes and see if they could help me figure out why this form is so slow to 1) open, 2) add a new line, and 3) select a client in the dropdown box in the "CLIENT" column. I've tried removing the page numbering in the layout:ready event and it doesn't help the performance at all. I do have a lot of calculations and a lot of resolveNodes, but I don't think I have any other way of performing these calculations without them. If anyone could point me in the right direction.

The form is here:

https://acrobat.com/#d=pBkuyHMzab36bS4N0U9VEg

...and if you open it in Acrobat or Reader first, you'll see what I mean. It's not as slow in a preview in Livecycle because all of the entered data is gone.

Thank you for any help.

Jo

1 Accepted Solution

Avatar

Correct answer by
Level 10

Hi,

I would like to try removing the two resolveNodes calls and the two item methods from Jono's example and see if that helped, so;

var result = 0;

var rows = this.resolveNodes("ItemSet[*]");

for (var i=0, limit=rows.length; i<limit; i++)

{

    var row = rows.item(i);

    if (row.client.rawValue === clientName.rawValue)

    {

        result += row.corresp.rawValue

    }

}

result;

But I have not timed this and am still surprised about being wrong with optimisations.

I think the big win is to remove the code from the calculate events of the the fields correspTotal, hcfaTotal, holdsMiscTotal, Total182, Total215, Total247, Total259 and Total268 (I renamed Total268 as it was a duplicate and complicated my script) and add the following code to the calculate event of clientTotals (the parent subform), so you only loop though your form once per client.

var correspTotalWork = 0;

var hcfaTotalWork = 0;

var holdsMiscTotalWork = 0;

var total182Work = 0;

var total215Work = 0;

var total247Work = 0;

var total259Work = 0;

var total268Work = 0;

var reps = P1.resolveNodes("rep[*]");

var repCount = reps.length;

for (var j=0; j<repCount; j++)

{

    var rep = reps.item(j);

    var itemSets = rep.resolveNodes("ItemSet[*]");

    var itemSetCount = itemSets.length;

    for (var i=0; i<itemSetCount; i++

    {

        var itemSet = itemSets.item(i);

        if (itemSet.client.rawValue === clientName.rawValue)

        {

            if (itemSet.corresp.rawValue > 0)

            {

                console.println(itemSet.client.rawValue+" "+itemSet.corresp.rawValue);

            }

            correspTotalWork += itemSet.corresp.rawValue;

            hcfaTotalWork += itemSet.hcfa.rawValue;

            holdsMiscTotalWork += itemSet.holdsMisc.rawValue;

            total182Work += itemSet.tic182.rawValue;

            total215Work += itemSet.tic215.rawValue;

            total247Work += itemSet.tic247.rawValue;

            total259Work += itemSet.tic259.rawValue;

            total268Work += itemSet.tic268.rawValue;

        }

    }

}

correspTotal.rawValue = correspTotalWork;

hcfaTotal.rawValue = hcfaTotalWork;

holdsMiscTotal.rawValue = holdsMiscTotalWork;

Total182.rawValue = total182Work;

Total215.rawValue = total215Work;

Total247.rawValue = total247Work;

Total259.rawValue = total259Work;

Total268.rawValue = total268Work;

The next step you could try would be to loop though the form once and maintain the eight totals per client.

To see the console messages in my previous example press Ctrl-J.  You should see some lines like;

form1.P1.rep.repTotals.Total268.#calculate:Time elapsed 3ms

form1.P1.rep.repTotals.unresolvedTotal.#calculate:Time elapsed 3ms

form1.clientTotals.correspTotal.#calculate:Time elapsed 340ms

form1.clientTotals.hcfaTotal.#calculate:Time elapsed 358ms

Good luck

Bruce

View solution in original post

26 Replies

Avatar

Level 10

Hi, Most of the processing time seems to be spent in calculating the clientTotals, you have 8 total fields all looping though the same structures, you could move this calculations to the clientTotals subform and save some of the looping.

This is an example of the form with some console messages giving the execution time for each event.  https://acrobat.com/#d=aZDO3dMHAiAda*IuGURmCQ

Each one of the clientTotals calculations are taking about 325ms on my machine, and you have three rows of eight fields.

Hope this helps

Bruce

Avatar

Employee

Here's the performance analysis of your form.

As Bruce indicated, you have violated the numericEdit is violating max allowable occurences. You should restrict the some loops.

Fields 43

Subform 12

Font Count : 43

calculate (JavaScript) : 153 (Line of Script)

enter (JavaScript) : 112 (Line of Script).

Avatar

Level 4

Thank you Bruce and Dpakch.

I looked at the form you created Bruce but I didn't see the consoles. Dpakch, any tips on how to restrict the SOM loops? I don't understand what you mean by "numericEdit is violating max allowable occurences".

I need to have totals by rep, but I also need totals by client at the bottom...with a form like this, is there any way to restrict the SOM loops? I found an article by John Brinkman that shows a way of doing it without using variables in the SOM expression. Problem is, I don't know how to set this up on my form. Do you know how to do this? Or if it's even possible?

Thanks,

Jo

Avatar

Level 10

If you don't have a specific requirement for JavaScript, FormCalc would speed things up the most and be a lot easier to write.

The asterisk in the square brackets is what handles repeating items.

Just looking at your repTotals.correspTotal field the FormCalc code would be:

$ = sum(ItemSet[*].corresp)

Or using the method John Brinkman shows with JavaScript:

var sum = 0;

var rows = this.resolveNodes("ItemSet[*].corresp");

var len = rows.length;

for (var i = 0; i < len; i++) {

          sum += rows.item(i).rawValue;

}

sum;

Avatar

Level 4

Thank you Jono.

The problem I'm running into with both the FormCalc and the JavaScript methods here is that I can't have all of the ItemSet.corresp values summed...only the ones that match the client name. This is because the number of instances of the repTotals subform and the repTotals.clientName values are dependant on the selection in the dropdown box at the top.

For example:

When the user selects "AZEM/PCEP" at the top, two instances of the repTotals subform are created, and repTotals.clientName[0] is set to "AZEM", and repTotals.clientName[1] is set to "PCEP". So I have to loop through the ItemSet subform, compare the repTotals.clientName to ItemSet.client, and sum only the corresp amounts where there is a match. How would you write a loop (efficiently) for that?

Thanks,

Jo

Avatar

Level 10

So expanding on John's sample I tried the following which worked but may not be the best way to go about it - hopefully Bruce will chime in, I'm not the greatest programmer.

It's definitely faster when I tested using Bruce's timer routine but I haven't had a chance to try it on the client totals section which is the one that's sucking most of the time - I think it should speed things up a fair bit there. Hopefully you can extrapolate this to work in your client totals section.

FormCalc would still be faster but I haven't done much with loops in FormCalc.

var sum = 0;

var rows = this.resolveNodes("ItemSet[*].corresp");

var oClient = this.resolveNodes("ItemSet[*].client");

var len = rows.length;

for (var i = 0; i < len; i++) {

          if (oClient.item(i).rawValue == clientName.rawValue) {

                    sum += rows.item(i).rawValue;

          }

}

sum;

Avatar

Employee

To increase further performance, you must not embed the fonts unless it an absolute neccessity. Adding subforms, allow to structure things properly and effective scripting. But adding too many subforms reduce performance. It's always a best to keep the count minimal.

One question, does your form require server side Data binding ? If not, uncheck the option under Form Properties.

Avatar

Correct answer by
Level 10

Hi,

I would like to try removing the two resolveNodes calls and the two item methods from Jono's example and see if that helped, so;

var result = 0;

var rows = this.resolveNodes("ItemSet[*]");

for (var i=0, limit=rows.length; i<limit; i++)

{

    var row = rows.item(i);

    if (row.client.rawValue === clientName.rawValue)

    {

        result += row.corresp.rawValue

    }

}

result;

But I have not timed this and am still surprised about being wrong with optimisations.

I think the big win is to remove the code from the calculate events of the the fields correspTotal, hcfaTotal, holdsMiscTotal, Total182, Total215, Total247, Total259 and Total268 (I renamed Total268 as it was a duplicate and complicated my script) and add the following code to the calculate event of clientTotals (the parent subform), so you only loop though your form once per client.

var correspTotalWork = 0;

var hcfaTotalWork = 0;

var holdsMiscTotalWork = 0;

var total182Work = 0;

var total215Work = 0;

var total247Work = 0;

var total259Work = 0;

var total268Work = 0;

var reps = P1.resolveNodes("rep[*]");

var repCount = reps.length;

for (var j=0; j<repCount; j++)

{

    var rep = reps.item(j);

    var itemSets = rep.resolveNodes("ItemSet[*]");

    var itemSetCount = itemSets.length;

    for (var i=0; i<itemSetCount; i++

    {

        var itemSet = itemSets.item(i);

        if (itemSet.client.rawValue === clientName.rawValue)

        {

            if (itemSet.corresp.rawValue > 0)

            {

                console.println(itemSet.client.rawValue+" "+itemSet.corresp.rawValue);

            }

            correspTotalWork += itemSet.corresp.rawValue;

            hcfaTotalWork += itemSet.hcfa.rawValue;

            holdsMiscTotalWork += itemSet.holdsMisc.rawValue;

            total182Work += itemSet.tic182.rawValue;

            total215Work += itemSet.tic215.rawValue;

            total247Work += itemSet.tic247.rawValue;

            total259Work += itemSet.tic259.rawValue;

            total268Work += itemSet.tic268.rawValue;

        }

    }

}

correspTotal.rawValue = correspTotalWork;

hcfaTotal.rawValue = hcfaTotalWork;

holdsMiscTotal.rawValue = holdsMiscTotalWork;

Total182.rawValue = total182Work;

Total215.rawValue = total215Work;

Total247.rawValue = total247Work;

Total259.rawValue = total259Work;

Total268.rawValue = total268Work;

The next step you could try would be to loop though the form once and maintain the eight totals per client.

To see the console messages in my previous example press Ctrl-J.  You should see some lines like;

form1.P1.rep.repTotals.Total268.#calculate:Time elapsed 3ms

form1.P1.rep.repTotals.unresolvedTotal.#calculate:Time elapsed 3ms

form1.clientTotals.correspTotal.#calculate:Time elapsed 340ms

form1.clientTotals.hcfaTotal.#calculate:Time elapsed 358ms

Good luck

Bruce

Avatar

Level 4

First of all, thank you all so much for helping me out with this....

Ok, so Jono's example works, but for some reason it doesn't include totals from any dynamically added rows in the ItemSet subform.

Took Deepak's advice and unchecked embed fonts and unchecked require server side data binding. I don't think those are required, so that may help.

Bruce's first suggestion also works, but like Jono's, only totals the initial line in the ItemSet subform.

Bruce's second suggestion sounds like it might be the best bet and save the most resources. And it works, however, I'm still running into not being able to include anything other than the 1st line of the ItemSet subform in the totals. If I can get past this issue, I might be all set. Any ideas on this one?

Jo

Avatar

Level 10

Hi Jo,

You may have to add in a reference to the instanceManager occurance number, see the second line.

var result = 0;

var limit = rep._ItemSet.count;

var rows = this.resolveNodes("ItemSet[*]");

for (var i=0, limit=rows.length; i<limit; i++)

{

    var row = rows.item(i);

    if (row.client.rawValue === clientName.rawValue)

    {

        result += row.corresp.rawValue

    }

}

result;

You don't have to do anything with the value but just referencing is enought to cause the calculate event to fire when it's value changes.

Also, I notice that all your fields are defined as float, if your values are all integer then you can define them as integer on the Binding tab of the Object palette.  Maybe it will handle integers quicker?

Bruce

Avatar

Level 4

That works! Thank you so much Bruce! The only lingering issue is that the Client Totals at the bottom do not update until/unless you add another "rep" subform. I guess this is because we moved the client totals calculations into the "clientTotals" subform, instead of within each field. ? Is there any way to get this calculation in the clientTotals subform to fire without the users having to add another rep subform? Here is the form as it has been updated with everyone's suggestions.

https://acrobat.com/#d=2S6NyFcXhRG0Xw2jEY9jBw

Bruce, you had mentioned in a previous post "The next step you could try would be to loop though the form once and maintain the eight totals per client." How would you do this? Would this fix the issue of it not immediately updating the clientTotals?

Edit: I found one way...may not be the best way, but it does work...I added the following FormCalc code to the exit event of each numeric field in the ItemSet subform:

form1.clientTotals[*].execCalculate();

If there's a better way though, I'll definitely try it, because I think this actually slows down the form again a little bit.

Jo

Avatar

Level 10

Hi,

Try moving the execCalculate call you added to the add line button, so

form1.P1.rep.Subform1.Button1::click - (JavaScript, client)

rep.ItemSet.instanceManager.addInstance();

P1.rep.execInitialize();

clientTotals.execCalculate();

Bruce

Avatar

Level 4

Thanks Bruce. That would probably work, but it isn't doing anything when I click. I think it may be because it should be:

clientTotals[*].execCalculate();

...so that it runs on ALL instances of the clientTotals subform. But that's FormCalc and I am using Javascript on this button already. Is there a way to use FormCalc and Javascript on the same object? Otherwise, is there a javascript alternative to this statement?


Thanks,

Jo

EDIT: I did find one way...placing it as FormCalc in the exit event of the ItemSet subform works. It basically doesn't calculate as soon as the user enters a number, but it does when they leave that line. I have also added it to the pre-save and pre-print events on the form in case the user prints or saves without leaving the subform also. It's messy, but it works ok.

Avatar

Level 10

You'd need a loop like the other loops but this time firing execCalculate. You can use FormCalc and JavaScript on the same object, just not the same event. So on a button you could use events like mouseUp/mouseDown/Click etc. to split things up. You could also have another hidden button that you fire with the first button.

Are things running much faster overall?

I was thinking some more about your form and depending on how large it gets you will probably still end up with things slowing down. The problem with using the calculate event is that every time something changes in the form the calculate event fires, so there's lots of wasted calculations going on.

One way around this may be to put your client total calculations onto a button that the user clicks when they want to refresh the totals, this would make things less frustrating for the person trying to do stuff in the form. And also as you noted above on the presave or preprint events.

Avatar

Level 4

Thank you Jono. Taking your advice, instead of creating another loop, I just went ahead and added a button in the clientTotals subform labeled "Update Totals" that runs the FormCalc event form1.clientTotals[*].execCalculate(); when clicked. This will be a final safeguard, almost a user reminder, to double check that all of the totals have been updated.

And YES, the form is absolutely running much quicker now:

The original form took 11 seconds to open. The new form takes 7 sec.

The original form took 7 seconds to add a line. The new form takes just over 1 sec.

The original form took 7 seconds to select a client in the dropdown box. The new form takes less than a second.

That updated code made a world of difference. Now I just need to study up on it because heaven forbid they need me to code something similar in the future! lol

Thank you all VERY much for all your assistance with this. You all have saved me!

Jo

Avatar

Level 4

Dragging up from the dead one of my own old posts...

The following Shared File (Shared Files - Acrobat.com) has served me well over the last couple of years, but as the list of clients has grown, so has the data entered, and once again, the form is becoming painfully slow by the time 20 or more pages have been added. What I would like to do is remove all automatic calculations from the "clientTotals" section, and only have that section calculate totals when "button3" ("Update Totals) is clicked. However, I must have gotten rusty in my javascript over the past couple of years because everything I am trying results in the button just doing nothing.

Does anyone know how to properly make this happen?

Thanks in advance.

Jo

Avatar

Level 10

Hi Jo,

I suspect the problem is you have code in each total field individually looping though the rows calculating the total. If you move the code to the subform object's calculate event then you will only have to loop though the rows once.

I have done the first two columns in this sample, https://sites.google.com/site/livecycledesignercookbooks/home/Log_Sheet.modified.pdf?attredirects=0&...

Adding the following code to the calculate event of the rep suform;


if (_repTotals.count > 0) // no team selected yet


{


var correspResult = {};


var hcfaResult = {};


var totalCorresp = 0;


var totalHCFA = 0;


var limit = rep._ItemSet.count;


var rows = rep.resolveNodes("ItemSet[*]");


for (var i=0, limit=rows.length; i<limit; i++)


{


     var row = rows.item(i);


     if (!row.client.isNull) // no client selected yet


     {


      correspResult[row.client.rawValue] = (correspResult[row.client.rawValue] === undefined) ? correspResult[row.client.rawValue] = row.corresp.rawValue : correspResult[row.client.rawValue] += row.corresp.rawValue;


      hcfaResult[row.client.rawValue] = (hcfaResult[row.client.rawValue] === undefined) ? hcfaResult[row.client.rawValue] = row.hcfa.rawValue : hcfaResult[row.client.rawValue] += row.hcfa.rawValue;


     }


}


var totalRows = rep.resolveNodes("repTotals[*]");


for (var i=0, limit=totalRows.length; i<limit; i++)


{


     var totalRow = totalRows.item(i);


     if (correspResult.hasOwnProperty(totalRow.clientName.rawValue))


     {


      totalRow.correspTotal.rawValue = correspResult[totalRow.clientName.rawValue]


     }


     if (hcfaResult.hasOwnProperty(totalRow.clientName.rawValue))


     {


      totalRow.hcfaTotal.rawValue = hcfaResult[totalRow.clientName.rawValue]


     }


     totalCorresp += totalRow.correspTotal.rawValue;


     totalHCFA += totalRow.hcfaTotal.rawValue;


}


repTotalTotals.repCorrespTotal.rawValue = totalCorresp;


repTotalTotals.repHcfaTotal.rawValue = totalHCFA;


}


I hope you can see the pattern.  If this still doesn't help then it will be the same code that goes into the click event of "button3".

Regards

Bruce

Avatar

Level 4

Bruce, thank you so much for your help. Your form worked great.

I've updated the rep subform to include all the other columns going by your example, but for some reason, in MY form, my repTotals subform only calculates for the first client selected in the list. For example, in my form, if you select the first Team at the top (ACES/SGEMA/SHI/VECM), and then in the first rep subform, if you select any client other than the first one, the totals do not populate in the repTotals subform (although they DO populate in the clientTotals subform).

Would you mind taking a look at my form and seeing what I did wrong?

Thank you so much!

Jo

Avatar

Level 10

Hi Jo,

Happy to have a look, but I can't see the form with the link you posted, there are 3 forms but none updated since April.

Bruce

Avatar

Level 4

Sorry...copied the wrong link. Try this one:

Shared Files - Acrobat.com

Thanks!

Jo