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
- City (repeating)
- States
- Selection
- SelectedState
- SelectedCity
- Data
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
- City (repeating)
- States
- Table
- Row (repeating)
- Selection
- SelectedState
- SelectedCity
- Notes
- Selection
- Row (repeating)
- Data
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
- Question (repeating)
- Questions
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]
Comments
Anonymous
April 14, 2005
I like InfoPath, but was a bit reserved at first... and as a developer I have found InfoPath immensely...Anonymous
March 19, 2007
The comment has been removedAnonymous
March 19, 2007
Have you seen these related blog posts? http://blogs.msdn.com/infopath/archive/2006/10/12/cascading-dropdowns-in-browser-forms.aspx http://blogs.msdn.com/infopath/archive/2006/10/12/cascading-dropdowns-in-browser-forms.aspxAnonymous
March 19, 2007
The second one was supposed to be: http://blogs.msdn.com/infopath/archive/2004/03/24/95529.aspxAnonymous
June 29, 2007
Will the form current()/preceding-sibling:: work for secondary data sources? I have a secondary data source repeating section whose xpath looks like this dfs:myFields/dfs:dataFields/tns:Get_ContactFlagsResponse/tns:Get_ContactFlagsResult/NewDataSet/Industries[1]/lu_Group , and I want to get the previous value of lu_group for conditional formatting. Any ideas?Anonymous
June 03, 2009
Hey, I have a question but didn`t see any recent post on the subject so i leaved it here hoping you discover it. I have a form where I need to apply several filters. I only use the drop down controls for this. Currently for my fifth filter, I have 4 conditions; value must equal what the user has entered in the previous 4 filters. This makes the respons periode slow. Is there any other way to do the filtering? Could filter 2 only search within results from filter 1, and consequently I dont need to apply the conditions? (the xml has in total about 27 000 rows) thanx OTHAnonymous
March 10, 2013
When using the current() function on my filtered drop downs (3 drop downs, Data Connection is an Access Database), all fields in my form become slow responding, including text fields outside of the repeating table which contains the drop downs. It takes a few seconds after a field is changed for the form to 'come back to life'. It seems like there is some background process going on after entering data into a text field, even though this should not be happening as the text field is not connected to any database. Does anyone know why this might be happening? Weirdly the form is ok if I swap the filtering information on the drop downs to have the form field on the left hand side (left of 'is equal to') and then have the access table field on the right hand side. However, if I do this I can then not use the current() function which I need.