Hi. It seems to me that in fact the issue has little to do with XSLT itself and all you need actually is to put your drop-down list inside the form with method="GET". And then set proper values to the option elements in the xslt that renders the <select> tag. Since the form has method="GET" a selected value will be sent to the server inside the query string.
I have the form using GET and sending the variables to the results page and if I just want to do exact matches I can get it to work. But I need it to work if someone sends through just one of the values. so If they only send through the area it won't try to use the property type and bedrooms in the search as this won't return any values.
This is what I got using that blog post. However when I try to save it I get errors
System.Xml.XmlException: Name cannot begin with the ' ' character, hexadecimal value 0x20. Line 29, position 67.
<!-- start writing XSLT --> <xsl:variable name="type" select="umbraco.library:RequestQueryString('type')"/> <xsl:variable name="area" select="umbraco.library:RequestQueryString('area')" /> <xsl:variable name="beds" select="umbraco.library:RequestQueryString('bedrooms')" /> <!-- ############ Build up the xpath portions (note: any variables or umbraco.library functions or other extensions must be inserted with xsl:value-of) ############## --> <xsl:variable name="distanceXpath" > string-length($type) > 0 and $type <xsl:value-of select="propertyType" /> <!-- just keep adding more conditions here to build up your xpath --> </xsl:variable> <!-- ############ End xpath portions ############## --> <!-- once you've got the xpath string, use ps:evaluateXpath and check that it returns 'True' to actually process the xpath statement --> <xsl:variable name="searchResults" select="$currentPage/ancestor-or-self::root//Property [ps:evaluateXpath(., $distanceXpath) = 'True']"/> <!-- <xsl:for-each select="$currentPage/ancestor-or-self::root//Property [@isDoc and leased != 1]">--> <xsl:for-each select="$searchResults" > <xsl:value-of select="@nodeName" /> <br /> <xsl:value-of select="propertyType" /> <br /> <xsl:value-of select="bedrooms" /> <br /> <xsl:value-of select="area" /> <br /> <br /> </xsl:for-each>
</xsl:template> <msxml:script language="C#" implements-prefix="ps" > <![CDATA[ public string evaluateXpath(XPathNodeIterator nodes, string xpath){ /* this is intended for a SINGLE node to be passed in, although the type is XPathNodeIterator; if multiple nodes are passed in, only the first one will be evaluated */ try{ while (nodes.MoveNext()){ XPathNavigator n = nodes.Current as XPathNavigator; return n.Evaluate(xpath).ToString(); } }catch{ return null; } return null; } ]]> </msxml:script> </xsl:stylesheet>
The main select in xsl:for-each means: "Select all descendant documents that have a property named $type with the value of $value". By the previous assignments the $type will be the 'type' from the query string and the $value will be either 'area' or 'beds' therefrom depending on the value of 'type'.
I get 3 values passed through to my results page, type, area and bedrooms. So I need to build the query string so it searches for type=propertyType AND area=propertyArea AND bedrooms = propertyBedrooms but only adding each condition if the passed through value != null
How do I make a dynamic xslt query
Hi,
I am building my first site in Umbraco and will admit that I am rubbish at Xslt.
I have a simple search form with 3 drop down options (propertytype, area, bedrooms).
I want my xslt to check if an option is empty and if it isn't then add the field to my query string.
I found this post http://www.blogfodder.co.uk/2009/12/22/building-a-dynamic-xpath-statement-in-xslt but I couldn't get this to work.
Any pointers would be greatfully received as it is starting to drive me a bit mad
Hi Rachael,
Could you show us some code, cause i'm not exactly sure what you're trying to do here. Have some examples or result you're aiming for?
Cheers,
/Dirk
Hi. It seems to me that in fact the issue has little to do with XSLT itself and all you need actually is to put your drop-down list inside the form with method="GET". And then set proper values to the option elements in the xslt that renders the <select> tag. Since the form has method="GET" a selected value will be sent to the server inside the query string.
I have the form using GET and sending the variables to the results page and if I just want to do exact matches I can get it to work. But I need it to work if someone sends through just one of the values. so If they only send through the area it won't try to use the property type and bedrooms in the search as this won't return any values.
This is what I got using that blog post. However when I try to save it I get errors
System.Xml.XmlException: Name cannot begin with the ' ' character, hexadecimal value 0x20. Line 29, position 67.
and/or
System.Xml.Xsl.XslTransformException: Extension functions cannot return null values.
apologies for my Xslt but this is the first time i've ever had to use it!
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:ps="urn:anynamehere:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:umbraco.contour="urn:umbraco.contour"
exclude-result-prefixes="ps msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets umbraco.contour ">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:template match="/">
<!-- start writing XSLT -->
<xsl:variable name="type" select="umbraco.library:RequestQueryString('type')"/>
<xsl:variable name="area" select="umbraco.library:RequestQueryString('area')" />
<xsl:variable name="beds" select="umbraco.library:RequestQueryString('bedrooms')" />
<!-- ############ Build up the xpath portions
(note: any variables or umbraco.library functions or other extensions must be inserted with xsl:value-of)
############## -->
<xsl:variable name="distanceXpath" >
string-length($type) > 0
and $type <xsl:value-of select="propertyType" />
<!-- just keep adding more conditions here to build up your xpath -->
</xsl:variable>
<!-- ############ End xpath portions ############## -->
<!-- once you've got the xpath string, use ps:evaluateXpath and check that it returns
'True' to actually process the xpath statement -->
<xsl:variable name="searchResults" select="$currentPage/ancestor-or-self::root//Property [ps:evaluateXpath(., $distanceXpath) = 'True']"/>
<!-- <xsl:for-each select="$currentPage/ancestor-or-self::root//Property [@isDoc and leased != 1]">-->
<xsl:for-each select="$searchResults" >
<xsl:value-of select="@nodeName" />
<br />
<xsl:value-of select="propertyType" />
<br />
<xsl:value-of select="bedrooms" />
<br />
<xsl:value-of select="area" />
<br /> <br />
</xsl:for-each>
</xsl:template>
<msxml:script language="C#" implements-prefix="ps" >
<![CDATA[
public string evaluateXpath(XPathNodeIterator nodes, string xpath){
/* this is intended for a SINGLE node to be passed in, although the type is XPathNodeIterator; if multiple
nodes are passed in, only the first one will be evaluated */
try{
while (nodes.MoveNext()){
XPathNavigator n = nodes.Current as XPathNavigator;
return n.Evaluate(xpath).ToString();
}
}catch{
return null;
}
return null;
}
]]>
</msxml:script>
</xsl:stylesheet>
Oh, well, probably i got what you want. If you need something like building an xpath query on-the-fly, usually a trick like this is helpfull:
The idea here is that you select all elements (*) or attriburtes (@*), but filter them by a local-name() = '...' condition
Sorry I really don't understand what I would do with that!
Rachael, am I right that you want to achieve this (the usual xslt header is omited):
The main select in xsl:for-each means: "Select all descendant documents that have a property named $type with the value of $value". By the previous assignments the $type will be the 'type' from the query string and the $value will be either 'area' or 'beds' therefrom depending on the value of 'type'.
sort of,
I get 3 values passed through to my results page, type, area and bedrooms. So I need to build the query string so it searches for type=propertyType AND area=propertyArea AND bedrooms = propertyBedrooms but only adding each condition if the passed through value != null
sorry for being a pain!
Ok, that's much clearer now. And it's turned out to be simpler than it seemed initially. So, change it this way:
Does it fit?
Hi, thank you that works perfectly
you have saved me from my third xslt induced nervous breakdown :)
is working on a reply...