I'm trying to filter a list of properties created as nodes, using a checkbox list on a page.
I've created a custom datatype called Property Checkbox List which has 10+ property features. The property documenttype enables the end user to select multiple features per property.
I have a page that displays a checkbox list using the following XSLT:
How do I get the first page to filter the property list on the 2nd page ? There is already a Form on the masterpage template, so I cannot add another on this childpage.
I currently have a list of Houses on a page. One of the custom properties of the House documenttype is Features (which the client can choose in the backend).
Typical Features would be Garden, Off Road Parking, Pool, Garage etc. The client could pick more than one Feature.
I would like to be able to filter those properties by the Features.
Property 1 (Garden, Garage)
Property 2 (Garden, Garage, Pool)
Property 3 (Garage, Off Road Parking)
The end user selects from a list of features, clicks Search and on the next page a list of properties are returned with those features.
i.e searching for Pool would only return Property 2, Garden would return Property 1 & 2.
xsl:variablename="nodes"select="$currentPage/node[@nodeTypeAlias='artworkPage' and string(data [@alias='umbracoNaviHide']) != '1' and ($artworkid = '' or @id = $artworkid) and ($collection = '' or data[@alias='collection'] = $collection) and ($category = '' or contains($category, data[@alias='category'])) and ($subject = '' or contains($subject, data[@alias='subject'])) and ($orientation = '' or contains($orientation, data[@alias='orientation'])) and ($rareorsoldout = '' or data[@alias='rare'] = $rareorsoldout or data[@alias='soldOut'] = $rareorsoldout)]" />
I can then just loop the nodes variable
<xsl:for-eachselect="$nodes"> ... </xsl:for-each>
One of the XSLT gurus may have a better way, but it worked for me =)
<xsl:variable name="destination" select="/macro/destination"/>
<xsl:template match="/">
<div id="propertySearch">
<!-- Variable gets the list of values from the datatype -->
<xsl:variable name="filterList" select="umbraco.library:GetPreValues('1089')"/>
<!-- TODO: This is the ID of the page to redirect to, needs hooking up to a variable -->
<form action="{umbraco.library:NiceUrl('1102')}">
<xsl:for-each select="$filterList//preValue">
<input type="checkbox" name="filter" value="{current()}" />
<label for="{@id}">
<option value="{@id}">
<xsl:value-of select="current()"/>
</option>
</label>
<br></br>
</xsl:for-each>
<input type="submit" class="filterButton" value="Search Properties" />
</form>
</div>
</xsl:template>
and this is the XSLT on the redirected page that filters the properties:
At the moment its only returning a match if the QueryString matches the filterBy variable exactly i.e:
Garden, Pool = Garden, Pool is True
Garden, Pool = Garden, Pool, Central Heating = False
What XSLT function do I need to use to "pick" out the relevant keywords from the QueryString. I'm using contains but this does not appear to work how I want it to. Do I need to split out the comma seperated list before checking for a match ?
You could use a recursive loop to see if all items are contained within the product attributes - but I reckon its a fairly bad way of doing it.
I suspect a better way round it might be to use a couple of EXSLT functions: intersection and difference. Basically get an intersection of search terms and product attributes, and then compare the intersection with the search terms using difference:
<xsl:variable name="searchvalues" select="umbraco.library:Split($seartchterms,',')"/> <xsl:variable name="productvalues" select="umbraco.library:Split($productattributes,',')"/> <xsl:variable name="intersection" select="Exslt.ExsltSets:intersection($searchvalues,$productvalues)"/> <xsl:variable name="test" select="Exslt.ExsltSets:difference($intersection,$searchvalues)"/> <if test="count($test/value) = 0"> This is a match </xsl:if>
I've not tested it, but in theory I think it will work. The premise is that if all the search terms are matched by the product values - the intersection will be the same as the search values (there could be more selected in the product values). So doing a difference on search terms and intersection should give no difference - if there is any (ie nodes in the difference set) - it is because there are terms in the search set that are not in the product set.
the only reason I suggested something a bit different is that doing it the way it was going seems to be an OR match. It checks if any of the search terms match any of the product attributes. So in Ian's example searching for Garden & Garage would return properties 1 and 2 only, rather than all 3 as they all have a Garage.
Of course the string comparison using contains should be faster, and is definitely the way to do it for an OR match - trying to match all of a subset of a checkbox list in Umbraco is quite tricky. I think a XSLT extension might be the best solution - or even a javascript function in the xslt file.
If you did do it with the for-each and contains test, then I think you would need to check the number of times it matches is equal to the number of search terms, which is a simple enough way forward.
Honestly, I quite like the idea of matching nodes using the intersect method - but I think only because it seems cool.
The way you've suggested there is probably the right way. Then you can do some useful stuff like ranking the nodes by how many search terms they match for an OR search. I might do it in a recursive template to give a number rather than a string length, but I like them.
With this method, in this example you can then say these properties match your requirements exactly, and these properties almost match; down to these which don't match.
Using Matt's logic will do the job - replace your inner for-each with his match variable and it should be cool. The following will make the UL of all nodes that match the criteria
it seems that you solved the problem, i tried your suggestions but no luck. the business case i have products(shoes) as nodes, and i have checkboxlist as a product propety(categortag) and i want filter the products based on selected check boxes, but no result returned, the search result page is empty, here is the code;
<div id="propertysearch"> <!-- Variable gets the list of values from the datatype --> <xsl:variable name="filterList" select="umbraco.library:GetPreValues('1350')"/> <!-- TODO: This is the ID of the page to redirect to, needs hooking up to a variable --> <form action="{umbraco.library:NiceUrl('1351')}"> <xsl:for-each select="$filterList//preValue"> <input type="checkbox" name="filter" value="{current()}"/> <label for="{@id}"> <!-- <option value="{@id}">--> <xsl:value-of select="current()"/> <!-- </option>--> </label>   </xsl:for-each> <input type="submit" class="filterButton" value="Search Properties" /> </form> </div> </xsl:template>
<xsl:template match="/" > <div id="products" class="productList" itemscope="" itemtype="http://schema.org/ItemList"> <!-- The following div#invokeXSLT is used by the update script to load the correct xslt when updating the UI --> <div class="invokeXSLT">productList.xslt</div>
Filter nodes by property
I'm trying to filter a list of properties created as nodes, using a checkbox list on a page.
I've created a custom datatype called Property Checkbox List which has 10+ property features. The property documenttype enables the end user to select multiple features per property.
I have a page that displays a checkbox list using the following XSLT:
I have an existing page that lists ALL the properties using this XSLT:
How do I get the first page to filter the property list on the 2nd page ? There is already a Form on the masterpage template, so I cannot add another on this childpage.
Not 100% clear on what you are asking here? Can you give us a user case or similar?
I currently have a list of Houses on a page. One of the custom properties of the House documenttype is Features (which the client can choose in the backend).
Typical Features would be Garden, Off Road Parking, Pool, Garage etc. The client could pick more than one Feature.
I would like to be able to filter those properties by the Features.
Property 1 (Garden, Garage)
Property 2 (Garden, Garage, Pool)
Property 3 (Garage, Off Road Parking)
The end user selects from a list of features, clicks Search and on the next page a list of properties are returned with those features.
i.e searching for Pool would only return Property 2, Garden would return Property 1 & 2.
This is how I've done it on a project of mine, it was geared towards artwork, but you could filter by criateria
I can then just loop the nodes variable
One of the XSLT gurus may have a better way, but it worked for me =)
Matt
Man, you can never predict how this forum is going to format your code. Applogies for the weird spacing.
Matt
Nearly there....
This is the code on the search criteria page:
and this is the XSLT on the redirected page that filters the properties:
At the moment its only returning a match if the QueryString matches the filterBy variable exactly i.e:
Garden, Pool = Garden, Pool is True
Garden, Pool = Garden, Pool, Central Heating = False
What XSLT function do I need to use to "pick" out the relevant keywords from the QueryString. I'm using contains but this does not appear to work how I want it to. Do I need to split out the comma seperated list before checking for a match ?
Hey Ian,
Do both your querystring and the facilities contain a comma seperated string? Or is one of them a single key value?
If they are both comma seperated, you may need to break up the querystring var into individual keywords, and do some kind of recusrive filter.
Many thanks
Matt
Yes they are both comma seperated strings, I've tried incorporating the code from here
http://our.umbraco.org/forum/developers/xslt/7981-Matching-multiple-comma-separated-values
in particular this peice
but I can't seem to get it to work.
You could use a recursive loop to see if all items are contained within the product attributes - but I reckon its a fairly bad way of doing it.
I suspect a better way round it might be to use a couple of EXSLT functions: intersection and difference. Basically get an intersection of search terms and product attributes, and then compare the intersection with the search terms using difference:
I've not tested it, but in theory I think it will work. The premise is that if all the search terms are matched by the product values - the intersection will be the same as the search values (there could be more selected in the product values). So doing a difference on search terms and intersection should give no difference - if there is any (ie nodes in the difference set) - it is because there are terms in the search set that are not in the product set.
/Josh
Hey Ian,
This worked for me (I added an additional comma on the end of the concat for $productattributes)
Josh, I haven't tried that before, but that sounds cool, will definatly give it a go
Matt
Hi Matt,
the only reason I suggested something a bit different is that doing it the way it was going seems to be an OR match. It checks if any of the search terms match any of the product attributes. So in Ian's example searching for Garden & Garage would return properties 1 and 2 only, rather than all 3 as they all have a Garage.
Of course the string comparison using contains should be faster, and is definitely the way to do it for an OR match - trying to match all of a subset of a checkbox list in Umbraco is quite tricky. I think a XSLT extension might be the best solution - or even a javascript function in the xslt file.
If you did do it with the for-each and contains test, then I think you would need to check the number of times it matches is equal to the number of search terms, which is a simple enough way forward.
/Josh
As a quick down and dirty AND check, you could do something like this
Josh, would you recommend something like this then over the method you suggested?
Matt
We're getting somewhere !! This is the code I have now:
Facilities =
Filter =
Matched
How do I now extract the information from the matched node ?
If I put:
after the Matched in the inner for-each loop, it doesn't return anything.
Sorry, should have said, spoke to my client and an OR search would be OK.
Matt,
Honestly, I quite like the idea of matching nodes using the intersect method - but I think only because it seems cool.
The way you've suggested there is probably the right way. Then you can do some useful stuff like ranking the nodes by how many search terms they match for an OR search. I might do it in a recursive template to give a number rather than a string length, but I like them.
With this method, in this example you can then say these properties match your requirements exactly, and these properties almost match; down to these which don't match.
Using Matt's logic will do the job - replace your inner for-each with his match variable and it should be cool. The following will make the UL of all nodes that match the criteria
For an OR match make the last test,
I'm getting this error when saving that XSLT
System.Xml.Xsl.XslLoadException: The variable or parameter 'productattributes' is either not defined or it is out of scope.
sorry - copy paste mistake - it should have been.
teach me to try and cut small corners!
and that'll teach me to copy paste without checking !
Fantastic, between you and Matt you've now got it working, thanks very much to both of you.
hi all,
it seems that you solved the problem, i tried your suggestions but no luck. the business case i have products(shoes) as nodes, and i have checkboxlist as a product propety(categortag) and i want filter the products based on selected check boxes, but no result returned, the search result page is empty, here is the code;
in the search page i call this xslts macro;
<xsl:param name="currentPage"/>
<!--<xsl:variable name="destination" select="/macro/destination"/>-->
<xsl:template match="/">
<div id="propertysearch">
<!-- Variable gets the list of values from the datatype -->
<xsl:variable name="filterList" select="umbraco.library:GetPreValues('1350')"/>
<!-- TODO: This is the ID of the page to redirect to, needs hooking up to a variable -->
<form action="{umbraco.library:NiceUrl('1351')}">
<xsl:for-each select="$filterList//preValue">
<input type="checkbox" name="filter" value="{current()}"/>
<label for="{@id}">
<!-- <option value="{@id}">-->
<xsl:value-of select="current()"/>
<!-- </option>-->
</label>  
</xsl:for-each>
<input type="submit" class="filterButton" value="Search Properties" />
</form>
</div>
</xsl:template>
and this is the the search result page calls;
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:include href="productList_product.xslt"/>
<xsl:param name="currentPage"/>
<xsl:variable name="filterBy" select="umbraco.library:RequestQueryString('filter')" />
<xsl:template match="/" >
<div id="products" class="productList" itemscope="" itemtype="http://schema.org/ItemList">
<!-- The following div#invokeXSLT is used by the update script to load the correct xslt when updating the UI -->
<div class="invokeXSLT">productList.xslt</div>
<xsl:variable name="propertyattributes" select="data [@alias = 'categorytag']"/>
<xsl:variable name="searchvalues" select="umbraco.library:Split($filterBy, ',')" />
<xsl:variable name="match">
<xsl:for-each select="$searchvalues/value">
<xsl:if test="contains(concat(',', $propertyattributes, ','), concat(',', string(.), ','))">1</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="count($searchvalues/value) = string-length($match)">
<li><xsl:value-of select="data [@alias = 'propertyName']"/></li>
</xsl:if>
</xsl:for-each>
</ul>
</div>
</xsl:template>
thanks,
fermat.
is working on a reply...