Filtering using the current() function

For some reason, the last week has been full of questions where the answer boils down to “use the current() function”.

 

The current() function is an XSLT XPath function which returns the current node. You can read the full W3C Recommendation if you want the nitty-gritty details, but if you wanted to read the specification you’d probably have done that instead of reading this blog.

 

You need this function in InfoPath if you’re trying to refer to data across repeating contexts. Some examples:

 

· Within a repeating table row, you want to build cascading drop-downs

· You want select or filter data from another context by data in the current row

· You want one row in a repeating table to refer to data in the previous row

 

These all come down to the same need – within an XPath expression, how do you say “this one”? That’s what current() is for. Whenever an XPath is being evaluated in a repeating context, current() returns the... well... current item.

 

Scenario: Within a repeating table row, you want to build cascading drop-downs

 

If your schema looked like this:

  • Root
    • Data
      • States
        • State (repeating)
      • Cities
        • City (repeating)
          • @state
    • Selection
      • SelectedState
      • SelectedCity

And you wanted to have drop-downs bound to State and City which select from the appropriate list, you can build cascading drop-downs using filters. You’d end up with a filter on the list-box items that looked like this:

/Root/Data/Cities/City[ @state = ../../../Selection/SelectedState ]

(If you used the interactive condition builder, simply select “The expression” in the first drop-down to show the expression as an XPath.)

 

Now let’s change the schema a bit to put the selections into a table – maybe with notes about each selection:

  • Root
    • Data
      • States
        • State (repeating)
      • Cities
        • City (repeating)
          • @state
    • Table
      • Row (repeating)
        • Selection
          • SelectedState
          • SelectedCity
        • Notes

If you try this in a Repeating Table row (/Root/Table/Row) you’ll find that it doesn’t work as expected:

/Root/Data/Cities/City[ @state = ../../../Table/Row/Selection/SelectedState ]

The XPath /Root/Table/Row/Selection/SelectedState - which for practical purposes here is the same as ../../../Table/Row/Selection/SelectedState - actually returns all of the states in all of the rows, and in XPath the predicate a[b = c] returns all a's where any b equals any c. If you parse that explanation carefully, you'll see that you get far more results than you were expecting. In this example, you get a list of all cities from any of the selected states! What you need is a way to say “just the current Selection/SelectedState”.

 

The fix then is to modify the XPath to read:

/Root/Data/Cities/City[ @state = current()/Selection/SelectedState ]

Scenario: You want select or filter data from another context by data in the current row

 

This is actually just a simpler version of the previous case.

 

Whereas a normal list of cities might be:

/Root/Data/Cities/City

And a static filtered list would be:

/Root/Data/Cities/City[ @state = "WA" ]

A dynamic filtered list would be:

/Root/Data/Cities/City[ @state = /Root/Selection/SelectedState ]

To pull the selection from the current table row the final XPath in the predicate needs to be made relative to the current row:

/Root/Data/Cities/City[ @state = current()/Selection/SelectedState ]

Scenario: You want one row in a repeating table to refer to data in the previous row

 

This one is fun – let’s say you had a quiz (a list of true/false questions) and you wanted to disable controls in the current question until the previous question was answered. You’d use Conditional Formatting to disable the controls.

 

If your schema looked something like this:

  • Root
    • Questions
      • Question (repeating)
        • QuestionText
        • AnswerText

Then within each row you can use this expression to disable the controls:

current()/preceding-sibling::Question/AnswerText = ""

To enter this in the condition builder, select “The expression” in the first drop-down.

 

(Note: In the example XPaths the namespace prefixes have been left out. Since InfoPath is extremely standards compliant - that is, nitpicky - when it comes to correct namespace usage you’ll need to include these.)

 

[Edited 2004-09-13 @ 10:37 AM - one of the XPaths was incorrect]