Microsoft InfoPath 2010
The official blog of the Microsoft InfoPath team

Conditional Default Values

Conditional Default Values

  • Comments 25

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

Attachment: ConditionalDefaultValues.xsn
Leave a Comment
  • Please add 3 and 6 and type the answer here:
  • Post
  • 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!

  • 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 = C

  • 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

  • The expression

    if (BoolCondition) {

      TrueResult

    } else {

      ElseResult

    }

    becomes

    (ElseResult | TrueResult ) [(BoolCondition) + 1]

    not

    (TrueResult | ElseResult ) [(BoolCondition) + 1]

  • 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.

  • 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!

  • 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.

    Scott

  • Thanks for this post.  Not much documentation out there for this.

  • 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?

  • Thanks for taking the time to write this! Really helpful.

Page 2 of 2 (25 items) 12