Conditional Default Values
Sometimes you want the default value of a field to be dependent upon a condition. However, there isn’t any direct functionality to support IF statements in the default values of fields. Substituting a rule for a default value only gets you so far, as the rule is only applied when that field that changes, not the fields that the rule depends on. Updating the field whenever any dependent field is changed would require you to copy the rule to each field. This is not very maintainable, so below I will describe two approaches to avoiding this.
The first approach is simple, but it has some limitations and caveats. The second approach is more complicated, but should work in all cases.
Method 1: Using the union and array indexer operators
The first approach is to use the union operator ‘|’ along with an array indexer ‘[]’ to select the proper value. For example,
if (BoolCondition) {
TrueResult
} else {
ElseResult
}
becomes
(TrueResult | ElseResult) [(BoolCondition) + 1]
You can see that (TrueResult | ElseResult) creates a node set, while [(BoolCondition) + 1] selects which node to choose. BoolCondition will evaluate to 0 or 1 (depending on its truth value). Then 1 is added because the node set is 1-based, not 0-based.
As a simple example, say that you want to set field3 to field2 if field2 is 10 greater than field1; otherwise set field3 to field1. On field3’s default value, the expression would be
(../my:field1 | ../my:field2)[( ../my:field2 > ../my:field1 + 10) + 1]
There are two caveats to using this approach:
1) The node set will always be returned in document order. It does not matter what the order is in the parenthesis, as (field1 | field2) == (field2 | field1). Since you cannot change the node set ordering, you may have to modify your BoolCondition to be NOT-ed. For more information on document order, you can visit the w3.org page on XPaths.
2) Inside of the parenthesis, you must only have nodes; you cannot have strings or any other type. So (field1 | “hello world”) will not work.
Method 2: Using concat, substring and string-length
To overcome these caveats, you can use the second approach here. That is to use concat, substring and string-length. For example, the same generic if statement from the previous approach converts to
concat(
substring(TrueResult, 1, (BoolCondition) * string-length(TrueResult)),
substring(ElseResult, 1, (not(BoolCondition)) * string-length(ElseResult)))
The key here is that BoolCondition will evaluate to 0 or 1. Therefore, the first substring will either take no characters (if BoolCondition is false), or it will take all of the characters (if it is true) of the TrueResult. Conversely, the second substring is evaluated with the “not” condition. Therefore, either the TrueResult or the ElseResult will be returned in their entirety, but not both.
Let’s say that we want to use the same example as the first approach above,
concat(
substring(../my:field2, 1, (../my:field2 > ../my:field1 + 10) * string-length(../my:field2)),
substring(../my:field1, 1, (not(../my:field2 > ../my:field1 + 10)) * string-length(../my:field1)))
The major advantage to this approach is that you can use strings, numbers or anything else as the TrueResult and ElseResult. For example, we could have placed “Success” and “Undefined” instead of ../my:field1 and ../my:field2.
That is all you need to create conditional statements in your default values! I'm attaching a sample form template that has this technique implemented (save the XSN locally before opening it). This method works on InfoPath 2003, 2007, and in browser-enabled form templates.
Thanks to Alexei Levenkov and Gary Hsu for their assistance on this article.
Nicholas Lovell
Software Design Engineer
Comments
Anonymous
November 27, 2006
There could be a third caveat for the first approach: it might cause extra postbacks in browser-enabled form templates (something to be verified). Form performance is always an important consideration for those so sometimes a more cumbersome method could yield fewer postbacks.Anonymous
February 20, 2007
Similar to performing elapsed time calculations, creating a new date or time value based on a previousAnonymous
March 22, 2007
When the BoolCondition is a bool xml element which can be null, the result of Method 2 will be an empty string. To resolved this problem, the condition should be modified like this: concat( substring("Yes", 1, not(not(IsTrue)) * 3), substring("No", 1, not(IsTrue) * 2) ) The double not() function will convert null value to false, then the result will be right.Anonymous
September 13, 2007
For selecting the default values this does work as desired, however if the user then changes the field value, it will revert back to the True or false value accordingly. There are no rules applied nor data validation being applied. Why will it not accept the new value and why would it possibly be revering back to resulting default value?Anonymous
January 12, 2008
Excellent post. Another caveat to Method 1....the expression is not supported by Form Server (MOSS 2007). The expression in Method 2 is however.Anonymous
August 18, 2008
UPDATE: Due to the number of requests for adding more columns to this sample, I have re-designed howAnonymous
April 07, 2009
Not sure if you can help me, but I am trying to work with a repeating table from a Batch data source and I want the table to automatically populate with values already existing on the form. The problem is that on this repeating table I cannot see on the Design side all the fields in the revolving table...It automatically picks up the field names from the Batch file when I do a preview. What i would like to be able to do is say: when FieldName="Title" then FieldValue=Field1 when FieldName="FirstName" then FieldValue=Field2 .... but it tells me that FieldName (it writes it as @Name) is not a valid field or group (probably because it is not assigned a specific value, it contains 4-5 rows). Is there any type of logic I can write to fix this issue? Thanks, ElenaAnonymous
April 07, 2009
Hi Elena, I provided an answer to this quesstion in the Submitting to a SharePoint list post. ScottAnonymous
August 25, 2009
The comment has been removedAnonymous
April 07, 2010
Hi (Scott ?) I adopted the example with the concat and substring function to the following: concat("Inbetriebnahme der "; tbxGerInfName; " Firewall des Herstellers "; tbxGerInfHersteller; "."; substring("Softwareupdate auf die Version " + tbxGerInfSWVersion; 1; (string-length(lbxGerInfSWUpdate) = 2) * string-length("Softwareupdate auf die Version " + tbxGerInfSWVersion)); substring(" "; 1; (not(string-length(lbxGerInfSWUpdate) = 2)) * string-length(" "))) But I always get following output: Inbetriebnahme der "tbxGerInfName" des Herstellers "tbxGerInfHersteller".NaN So the first sentence is absolutly right. But what's the meening of "NaN" in the second sentence? WalterAnonymous
April 07, 2010
Hi Walter, "NaN" means "Not a Number" - so your expression is returning something that is not recognized as numeric value. ScottAnonymous
April 07, 2010
OK, and do you know what might be the reason for this in the snippet above? WalterAnonymous
April 09, 2010
Hi Walter, Not offhand...so I would suggest starting fresh with your expression and just bring in the parts piece-by-piece to see where it fails. ScottAnonymous
July 08, 2010
The comment has been removedAnonymous
March 04, 2011
The disconnect between InfoPath 2010 and SharePoint seems very large. IF statement functionality is allowed for default values in Sharepoint list fields. Simply opening a form in InfoPath, making a minor layout change and publishing, breaks your default value because InfoPath 2010 does not allow IF statement functionality. Metadata fields don't even try to work in InfoPath 2010. As a customer, it's very disheartening when you want to make a simple change in Sharepoint and it forces you to open up another tool (in this case InfoPath), and then the tool does not have what the user sees as basic functionality, or the functionality is completely different. It seems like the Sharepoint and InfoPath teams at Microsoft are working independently of each other, and there's a major disjoint between the products. Which, a user is expecting to be seemless between the products because SP forces InfoPath on the user.Anonymous
March 08, 2011
Here's a formula for the name of a day of the week, using the above technique, in case anyone could use it: concat(substring("Sunday", 1, (@DayOfWeek = 1) * string-length("Sunday")), substring("Monday", 1, (@DayOfWeek = 2) * string-length("Monday")), substring("Tuesday", 1, (@DayOfWeek = 3) * string-length("Tuesday")), substring("Wednesday", 1, (@DayOfWeek = 4) * string-length("Wednesday")), substring("Thursday", 1, (@DayOfWeek = 5) * string-length("Thursday")), substring("Friday", 1, (@DayOfWeek = 6) * string-length("Friday")), substring("Saturday", 1, (@DayOfWeek = 7) * string-length("Saturday"))) I wish InfoPath didn't make us jump through such awkward hoops to accomplish simple stuff like this!Anonymous
June 19, 2011
How do I go about an situation like the following I want to create a conditional expression which will give a result with the following conditions If field1 < 29 = A If field1 >29 and <39 = B If field1 >39 and <49 = CAnonymous
June 19, 2011
Hi Joe, This example will only work when there is one test and the result is true or false...in your example, there are multiple tests. So the best way to accomplish what you need is to use a Rule on field1 (with 3 conditions) that sets the value of field2 based on the conditions you have in the Rule. Here is what I have as a sample:
- Main Data Source: myFields field1 field2
- 3 Rules on field1 as follows: Rule 1: Condition: field1 < 29 Action: Set a field's value: field2 = "A" Rule 2: Condition: field1 > 29 and field1 < 39 Action: Set a field's value: field2 = "B" Rule 3: Condition: field1 > 39 and field1 < 49 Action: Set a field's value: field2 = "C" Scott
Anonymous
June 28, 2011
The expression if (BoolCondition) { TrueResult } else { ElseResult } becomes (ElseResult | TrueResult ) [(BoolCondition) + 1] not (TrueResult | ElseResult ) [(BoolCondition) + 1]Anonymous
June 29, 2011
Actually, (ElseResult | TrueResult) == (TrueResult | ElseResult). Check out the caveat noted above: ... The node set will always be returned in document order. It does not matter what the order is in the parenthesis, as (field1 | field2) == (field2 | field1). Since you cannot change the node set ordering, you may have to modify your BoolCondition to be NOT-ed. For more information on document order, you can visit the w3.org page on XPaths.Anonymous
November 30, 2011
I am trying to do something similar but can't figure it out. If Field A contains data display in Field C otherwise display Field B. Can you give me an example to resolve please. Many thanks!Anonymous
December 01, 2011
Hi Corrie, I should be able to assist but I would like to get a bit more information. So you have 3 fields on your View: FieldA, FieldB, FieldC. If "FieldA" contains data, you want to display that data in "FieldC"? Then if "FieldA" is blank you want to display "FieldB"? I just want to be sure I follow what you are trying to accomplish. ScottAnonymous
May 10, 2012
Thanks for this post. Not much documentation out there for this.Anonymous
July 24, 2013
I am designing a form in Microsoft Infopath. I need itself to automatically generate the end of project date once I type the date of start and duration of the projects in months. For instance, my project starts on 25/07/2013 (Field-1) for the duration of 3 months (Field-2). What should the formula be in the Field-3, so that project end date is automatically generated in Field-3?Anonymous
March 18, 2015
Thanks for taking the time to write this! Really helpful.