FormCalc Expressions (If and For)
In Designer, you can write scripts using one of two languages: FormCalc and JavaScript. If you’re like me, you’re more familiar with JavaScript because it’s very similar to Java and JScript syntax but when it comes to FormCalc, you’re lost. Well, not completely but you find it very difficult to find documentation on the syntax for loops (like While, For, etc.) which are sometimes essential. Maybe you fall-back on JavaScript in those times but, on average, FormCalc scripts execute much faster than JavaScript scripts so why not harness that performance in our forms?
In this post, I’ll explain the syntax of the If and For expressions in FormCalc. Those of you familiar with Visual Basic or VBScript will likely find this more familiar than the equivalent JavaScript syntax (while I’ll include for comparison however this isn’t a post on JavaScript syntax). In future posts, I’ll add to this collection by discussing the syntax for While and ForEach loops.
Conventions
Before we get started, I should explain a few things about the various syntax blocks you’ll find further down in this post (note that this is simplified for the purposes of this article):
- SimpleExpression: A simple expression like a comparison “i >= 0” that evaluates to true or false or a single entity like “MyVar” (a defined variable) or “5” (a number), for example.
- ExpressionList: A set of 1 or more expressions, each on a separate line.
- Square Brackets: A series of keywords (like “if”, “then”, “for”, “while”, etc.), SimpleExpressions and ExpressionLists grouped within square brackets denote an optional part of the syntax.
- Asterisk Character: Sometimes a closing square bracket is followed by an asterisk (*). This indicates that the optional syntax may be used 0 or more times.
- Question Mark Character: Sometimes a closing square bracket is followed by a question mark (?). This indicates that the optional syntax must be used 1 or more times.
Variables
I assume you’re probably familiar with declaring variables in scripts but just in case, in FormCalc, you declare variables like this:
var MyVar
and you can even declare it and assign it a value in the same step:
var MyVar = 5
If Expression
Let’s look at one of the most essential of all expressions:
FormCalc Syntax
if ( SimpleExpression ) then
ExpressionList
[ elseif ( SimpleExpression ) then
ExpressionList]*
[ else
ExpressionList]?
endif
Based on the syntax, you can do things like (assuming you’ve declared a variable named “i”):
if (i >= 10) then
i = i + 10
endif
or
if (i >= 10) then
i = i + 10
elseif (i >= 5) then
i = i + 5
i = i * 2
else
i = i + 1
endif
Hopefully this illustrates the differences in the syntax indicated by the asterisk and question mark characters.
JavaScript Syntax
For comparison, let’s look at the equivalent JavaScript syntax for the two examples above (note the semicolons and curly braces):
if (i >= 10)
i = i + 10;
or
if (i >= 10)
i = i + 10;
else if (i >= 5)
{
i = i + 5;
i = i * 2;
}
else
i = i + 1;
For Expression
Often times, in your scripts, you need to execute some statements a certain number of times or iterate through a collection. In those cases (and many others), a For loop may be useful.
FormCalc Syntax
for Variable = StartExpression
(upto | downto) EndExpression
[step StepExpression]?
do
ExpressionList
endfor
In this syntax, there’s a StartExpression, and EndEExpression and a StepExpression. These are all similar to SimpleExpressions as described earlier. One thing to note about For loops in FormCalc is that “upto” loop will iterate as long as Variable is less-than-or-equal-to (<=) EndExpression whereas a “downto” loop will keep iterating as long as Variable is greater-than-or-equal-to (>=) EndExpression. Also, the variable Variable is implicitely declared in the For expression.
Based on the syntax, you can do things like add the numbers 1 to 10, inclusively, to a list:
for i = 1 upto 10 do
MyList.addItem(i)
endfor
and then
var nCount = MyList.#items[0].nodes.length
if (nCount >= 2) then
for nItem = nCount downto 1 step 2 do
xfa.host.messageBox( MyList.#items[0].nodes.item(nItem - 1) )
endfor
endif
which would display the value of each even-numbered list item in reverse sequence using a message box as long as there’s at least one even-numbered list item to display.
JavaScript Syntax
For comparison, here are the equivalent JavaScript examples (note the semicolons and curly braces again):
for (var i = 1; i <= 10; i++)
{
MyList.addItem(i);
}
(in this case, the curly braces are optional but I think this is better style) and then
var nCount = MyList.resolveNode("#items[0]").nodes.length;
if (nCount >= 2)
{
for (var nItem = nCount; nItem >= 1; nItem - 2)
{
xfa.host.messageBox(
MyList.resolveNode("#items[0]").nodes.item(nItem - 1) );
}
}
Posted by Stefan Cameron on September 14th, 2006
Filed under FormCalc
Both comments and pings are currently closed.
I agree that it’s more readable to have brackets in the javascript IF statement even if it’s a single statement, but I prefer to hide the first bracket because then it reduces the lines of code to make it more readable for me.
if (nCount >= 2) {
for (var nItem = nCount; nItem >= 1; nItem – 2) {
xfa.host.messageBox(
MyList.resolveNode(“#items[0]”).nodes.item(nItem – 1) );
}
}
Not sure where to post this on this site so figured this looked best. I am trying to follow a tutorial you had posted a while back and cannot get it to work.
Basically I need someone to choose a value from a drop down list (which is pulling from an oracle db). Then based on that drop down I want it to populate other fields on the screen. Right now I have 2 fields on the screen ZZ_CODE and ZZ_DESC. When they choose ZZ_CODE I want it to populate ZZ_DESC.
Here is my script:
—– form1.#subform[0].Body.ZZ_CODE::change: – (FormCalc, client) ———————————
var sCODE = xfa.event.newText
var sZZ_CODE = $.boundItem(sCODE)
var oDataConn = Ref(xfa.sourceSet.AbraData)
oDataConn.#command.query.commandType=“text”
oDataConn.#command.query.select = Concat(“Select distinct ZZ_DESC from PS_ZZ_HRTABLES Where ZZ_CODE = “, sZZ_CODE,””)
oDataConn.open()
oDataConn.first()
AbraSub.presence=”visible”
AbraSub.ZZ_DESC = sCODE
I get an Script Failed error – Error: syntax error near token ”
If I can get this working I would then like to be able to take multiple fields into my where clause to populate the other fields on the form. For example, i will have a country code and a state code which will drive combined the data that shows on the form. So where clause like so ” Where state = “, sZZ_State,” and country = “,sZZ_COUNT,””)
Any help would be greatly appreciated. In fact I could even fax you a beer! 🙂
Greg Blodgett,
I’m wondering if the syntax error you’re getting is due to the double-double-quotes you have as the 3rd parameter to the call to the Concat function since in FormCalc, two double-quotes in a row evaluates to a single double-quote (that’s how you would get a double-quote into a string, for example).
Since you’re only concatenating two strings, you don’t really need the 3rd parameter so just try it like this:
Let me know if that solves the problem. I’ll get my fax machine ready just in case… 😉
Stefan,
I have completed mutilated my code since i posted. So now I have done this below going by what I found in Adobe documentation:
I now do not get an error but my other fields are not updating. My drop down list is populating correctly but when I choose something nothing else changes on the screen.
I am also getting an Oracle error listener could not resolve SERVICE_NAME. This error is odd because I can login through the Data View and it pulls back the tables but when I
preview I get the Oracle error – weird…I will have to buy more beer for this one!
—– form1.#subform[0].ZZ_EEID::initialize: – (JavaScript, client) ——————————–
var sDataConnectionName = “AbraData”;
var sColHiddenValue = “ZZ_EEID”;
var sColDisplayText = “ZZ_EEID”;
// 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”
nIndex = 0;
while(oDB.nodes.item(nIndex).className != “command”)
{
nIndex++;
}
// Need to set BOF and EOF to stay
oDB.nodes.item(nIndex).query.recordSet.setAttribute(“stayBOF”, “bofAction”);
oDB.nodes.item(nIndex).query.recordSet.setAttribute(“stayEOF”, “eofAction”);
// 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
Greg Blodgett,
Your script was cut-off because of a “less-than” character interpreted as HTML code. Please re-post your comment making sure that any less-than (“<“) characters are replaced by “<” (without the quotes).
Hope this works I tried another time and it still cut it off.
Thanks for all your help. So Stefan are you available for consulting?
—– form1.#subform[0].ZZ_EEID::initialize: – (JavaScript, client) ——————————–
var sDataConnectionName = “AbraData”;
var sColHiddenValue = “ZZ_EEID”;
var sColDisplayText = “ZZ_EEID”;
// 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”
nIndex = 0;
while(oDB.nodes.item(nIndex).className != “command”)
{
nIndex++;
}
// Need to set BOF and EOF to stay
oDB.nodes.item(nIndex).query.recordSet.setAttribute(“stayBOF”, “bofAction”);
oDB.nodes.item(nIndex).query.recordSet.setAttribute(“stayEOF”, “eofAction”);
// 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)
{
o
ValueNode = oRecord.nodes.item(nColIndex);
}
else if(oRecord.nodes.item(nColIndex).name == sColDisplayText)
{
o
TextNode = oRecord.nodes.item(nColIndex);
}
}
while(!oDB.isEOF())
{
this.addItem(oValueNode.value, oValueNode.value);
//IDList.addItem(oValueNode.value, oTextNode.value);
oDB.next();
}
// Close connection
oDB.close();
—– form1.#subform[0].ZZ_EEID::change: – (FormCalc, client) ————————————–
var sZZ_EEID = xfa.event.newText
var oDataConn = Ref(xfa.sourceSet.AbraData)
oDataConn.#command.query.commandType=”text”
oDataConn.#command.query.select = Concat(“Select * from SYSADM.PS_ZZ_RATING Where ZZ_EEID = “, Ltrim(Rtrim(sZZ_EEID)))
Greg Blodgett,
Your comment got flagged as spam. I suppose it was because of all the script. Luckily it was long enough that I noticed it as I was skimming my spam list (I get hundreds per day). 🙂
I will try to respond as soon as I can.
As for consulting, I’m not available for that unless you catch me at a conference… Otherwise, my primary duty is development on LC Designer and this blog is just something I run on the side. I can recommend a consulting service if you like.
Greg Blodgett,
As far as I can tell, you Initialize script looks OK. As for your Change script, based on what you posted, you’re missing the following statements after you set the data connection’s select statement:
That should explain why fields bound to the second data connection remain blank after you select an item in the drop down list.
Also, if you are editing this form with Designer 8.0 and have saved is as an Acrobat 8 PDF (XFA 2.5) form, then you also have another problem with your Change script: You’ll first need to clone the data connection before you set its commandType and select properties. Otherwise, you’ll get a security error.
Please give these tips a try and let me know if you get your form working.
I am using ADOBE form with SAP to generate a Report.
When amount field is set with display pattern it is showing some weird character like ¤17.10.
Is it possible to hide this character using FormCalc expression.
Please, advice.
Rachel,
Can you simply modify the Display Pattern to remove the symbol (which looks like a foreign currency symbol to me)?
Or are you wanting to modify the Display Pattern with FormCalc script?
Hi Stefan,
I’ve been trying to figure something out and came across your post from April 15th where you asked what I’m looking for:
Is it possible to modify the Display Pattern with FormCalc or JavaScript?
Here’s what I’m trying to accomplish programmatically:
1. User enters value of 1-10 into field. I’ll use “3” for the value.
2. After the users exits the field, the display pattern is changed to “three (3)”
I know I can use something like a switch case statement to change the value of the field to what I want it to be, but I’d rather use a display pattern so the user can only enter a value between 1-10.
Does that make sense? If not, please let me know if you need more info.
Thanks,
Jeff
Jeff,
Unfortunately, I don’t believe the XFA Picture Clause spec provides a way to do conditional formatting in such a way that you could specify to a particular sub-pattern within a compound pattern like “num{‘One x’}|num{‘Two x’}” where it would choose “One 1” if x was 1.
I think your best bet is to use the Change event script to filter the input and set the output.
Thanks for your reply, Stefan. I figured as much, so I did end up using the Change event script as you suggested.
Jeff
Hi, Stefan
Could you, please, help me with one issue relater FormCalc for?
I’m taking some list by this code:
var userCode= xfa.resolveNodes(concat(“$record.empls.empl.[fg_code==”””,fgCode,”””].empl_code”))
and now i need loop throught this userCode list, I tried to do it by:
for i=0 upto comboLen -1 step 1 do
xfa.host.messageBox(userCode[i])
endfor
but no luck.
How can I loop formCalc list variable?
Thank you in advance,
Paul
Paul,
I think the problem might be in the way you’re attempting to dereference the array to access an item within it.
Try “userCode.item(i)” instead of “userCode[i]”. Node list objects don’t have a “[]” operator…
Hi Stefan,
Can you help me in converting the following Javascript line to FormCalc-
var fields = xfa.layout.pageContent(xfa.layout.page(this)-1, “field”, 0);
manzoor,
Just replace “this” with “$”.
Hi –
Is there a maximum number of lines that can be executed inside of a for loop?
Grasping at straws here, so figured it was worth a shot!
Thanks,
Sharon
Sharon McClure,
Not really. If script execution is stopping after a certain number of lines within the loop, it’s more likely that you have an error in your script on a particular line (or a condition occurs where your script fails) than that you’re hitting a maximum line limit.
Hi Stefan,
I am a beginner with FormCalc and am trying to create a form such that if a particular field has no value, another field is hidden. If the first field has any value, then the subsequent field is shown with a drop-down list and so on. The first sequence works but when I enter text in the first field, I automatically get “visible” in the second field in addition to my drop down list ! How can I just have the field become visible, but not put the word in there too ? I tried using the rawValue function as well as using a default in the object value, but it didn’t make a difference. Here is the script. Much thanks !
—– F.P1.work2::calculate – (FormCalc, both) ———
if (volunteer2.isNull | volunteer2 == “”)then
work2.presence = “invisible”;
else
work2.presence = “visible”;
work2.rawValue = work2.rawValue
endif
Sri,
Calculate events are not meant to be used for changing a form’s layout like showing/hiding fields. They are intended for data calculations like sums and string concatenations. Also, “isNull” doesn’t exist in FormCalc, as far as I can tell from the spec.
To achieve what you want, you could use the volunteer2 field’s Exit event which gets executed when focus leaves the field (user clicks away or tabs out). This script would check the value of the field and, if not empty, would show the work2 field, otherwise it would hide it:
Hi,
I have requirement as.
Form will contain First Page with list of equipments in a table (Rows count is dynamic depending upon number of equipments). Other Pages will contain Individual Equipments.
First Page contains Table with Columns 1. Equipment No. 2. CheckBox.
The User will selects CheckBox & Press Button “Ok”. At this point it Should pickup Equipment in selected row & control should be moved to respective Page.
How we can get details of selected Row Dynamically?
Any help will be greatly appreciated.
Thanks & Regards
Mahendra Khandagale
Hi ,
I have requirement of Printing footer only on the last page of the printed form.
I have a standard static text window in my adobe form.
Suppose the static text name is ‘FOOTER2’ .
Can you please provide a sample code in formcalc to achieve this functionality.
Also what are the standard variables in Adobe Live cycle designer that have the details about the Total no of pages and ‘Current page’ of the form being displayed.
Thanks
mahendra,
Are you asking me how to automatically scroll down to where the equipment line is or how, upon checking the box, to get information about the related equipment?
Abhishek Kokate,
For the footer issue, there are a few ways you could go about it but the easiest is to simply place that footer at the last position, in the hierarchy, on the body pages (i.e. “Design View”). As long as all content before it flows, the footer will naturally flow onto the last page of the form.
The other way to do it requires Acrobat 8.1 or later and uses the simplex/duplex printing features. To activate this, you would set the “Object palette > Page Set tab > Printing property” to something other than “Page Occurrence”. Then, you would create a new master page, put your footer at the bottom of it (resizing the content area such that it doesn’t overstep the footer) and set the master page to be used only on the last page by setting the “Object palette > Pagination tab > Placement property” to “Last Page”.
As for getting the current page and page count, you can get those by using some xfa.layout API methods. The total page count can be obtained by using xfa.layout.pageCount() however the current page number can only be obtained from a reference object. Given an object (that’s visible), you would use xfa.layout.page(objRef). Note that both method calls can only be done in a Layout:Ready event (after the form has been fully rendered) otherwise page information will be incorrect.
Hi Stefan,
I have my footer at the last position, in the hierarchy,just after the table,so as the content of the table before it flows, the footer is been pushed to the last page.
Now my requirement is to print the page number on every page at the right bottom corner , so please tell me how to print the page number at that positon
Hi Stefan,
I am new to formcalc..Can you please help me how can i proceed?
I have the requirement in my PDF to have all important data(that i have done) and at end of form there is one email submit button and one input field in which user will enter the email id and then press the email submit button to send this pdf via email to that emailid, can you please tell me how i can do this..please reply immediately
Neha,
I have a tutorial on email submit buttons that will help you do what you describe. Note that the tutorial uses JavaScript, not FormCalc, however you can easily set the script language in the Script Editor.
Hi Stefan…thanks for ur immediate reply…
i have placed the button (set its properties as Submit) and as my requirement is to send PDF so i set the submit as property to ‘PDF’.
Now the case is i have one changeable field on pdf which will have email address coming from my interface or user can enter his own email address as well, so the requirement is to send the PDF to all those email addresses entered by the user in that changeable field, so in this case wat wud be my URL string ? and also for this submit button ‘click’ event is coming disabled at my end…Please advise
Thanks
Neha
Neha,
Please see my response to your comment on my email submit button tutorial.
Hi there, I am very new to Livecycle and only just dabbling. I have a form with a drop down list in a table from which the user will select a licence type.
Then depending on the licence type I need another drop down list to change accordingly.
e.g If dropdown1 = Bird Licence then
populate method drop down list with Observe
populate method drop down list with Nest Visit
elsif dropdown2 = Bat Licence then
populate method drop down list with By Hand
populate method drop down list with Net
I am using javascript and populating from a db source is not an option right now.
I’d much appreciate some guidance as to whether this can be done at run time. Hope I have given enough information
Ooh – I am using Adobe Livecycle Designer 8.0
Many thanks,
Bev.
Bev Williams,
My tutorial on handling list selection changes should tell you everything you need to know about detecting a change in selection in a drop down list. Once you have detected the change, you can change the items in another list by first clearing the existing items, using the ‘clearItems()’ method on the list, then by adding new items, using the ‘addItem(text[, value])’ method on the list.
If you can hook this up to a database as the source, then you should have a look at my tutorial on selecting specific database records.
Hi Stefan,Iam working on adobe life cycle designer.I have to display multiple images at run time from external source that has no URL.Iam using a Imagefield and need to assign the values using FormCalc.Pls advice.(Iam using life cycledesigner 8.0)
@prasanth,
I’m afraid you’re going to run into security issues that will make it difficult for you to get those images.
Can you elaborate on where they’re located? User’s local system?
Generally speaking, unless you can get the base64-encoded image bytes for those images through some data connection or web site, you will not be able to load them into your form unless a user manually selects them from some physical location.
Hi Stefan,
The Images are stored in DMS and Iam able to pull them into an internal table of type XSTRING.When I try to map this table to an Image field, the images are not displayed. But when I try with a single variable and map it to Graphic node, it works fine.Iam looking for an option on how I can map a table to graphic node ( probably through scripting).
@prasanth,
When you talk about a “Graphic node”, I take it you’re not referring to an XFA form…
To get these images into your form, you’ll need to get their base64-encoded byte strings into some table, make a data connection to that table, and bind the image byte column to a repeatable image field (i.e. an image field in a repeatable subform) on your form.
oh, Dear, Sir,
horrible, I can´t find good FormCalc hints:
my wish:
when an user entered a drop-down-field or
especcially a calendar-element:
open the list withoutr cklicking to the button, you know!?
I believe it into the enter-event,
but…
…$.open.show…
please, some snippeds or a very good link
inner join stefcameron…?!
ThankYouVeryMuch! Yours Kurt Lukas, Europe, Germany, Bayern.
@Mr. K Luk,
You can force the list part of a drop down list to open programmatically by using the xfa.host.openList({object}) function where {object} is a reference to the drop down list.
To do this when the user clicks into the drop down list, you can call the function in the field’s Enter event which occurs when the field receives input focus.
Hi Stefan,
I have two images at the same location on the master page. I need to print the first image if the current page is not equal to number of pages and if the current page equals to number of pages then print the other image.
I have a table in the main window that overflows through n number of pages. I tried it using the java script but it did not work for me. Can you please provide me some inputs.
Thanks,
Kiran.
@Kiran,
This can be done quite simply using script that identifies the current page out of the number of pages. Knowing that each page gets an instance of the master page, you just need script in a hidden button, for example, on the same master page as the two images that does this:
The code above will leave Image1 visible on all pages except the last one where Image2 will be the only visible image.
Hi Stefan,
I am just trytin to create a form in Adobe designer. My problem is what follows:
I have the followin expression:
F.P2.numCiswitch::calculate – (FormCalc, client) ———————————————
if F.P1.numCurrency ne 2 then
F.P2.numCiswitch.rawValue=45
elseif
F.P1.numCurrency ne 1
F.P2.numCiswitch.rawValue= 64.5
endif
But I want also the possibility that an user can modify this prices, but even if I entered “user entered- recommended” it does not work, because once I click the enter it goes again to the if expression values.
Thanks,
Ainhoa
@Ainhoa,
You need to set the “Object palette > Value tab > Type property” to “Calculated — User can override” and choose “script” as the source for the calculation.
Hi Stefan
Thank you for your reply, but I continue with the same problem, I did what you said but when I want to change the calculation value it does not allow. If i change Run at and select ” client” then I can change it, but the calculation does not work any longer.
Thnks
@Ainhoa,
Once the value is overriden by the user, the calculation won’t run anymore.
Also note the bug on calculation overrides. It was fixed in Acrobat/Reader 8.0.