I'm building a page that has some filter boxes on (check boxes). There are four sets of filters.
On my page I have a macro that reads in the values of query strings sent to the page and then displays the results accordingly. I have got this to work just fine using a set of and statements in an apply-template.
The problem I now have though is that it is too precise. To get a result back, I have to to check everything and then uncheck the one box I don't want to filter on (because of the and filters). It also means that when the page loads, I get no results and I would like to load everything by default.
My ideal situation would be to test to see if the query string exists, if it does include it in the filter. If it doesn't exist, don't filter on it. I could probably do this with 16 seperate if statements where I pop a different apply-tempate in each one, but that sounds ridiculous to me.
My original code is below (I have only included two filters to keep the code to a minimum on here). What I have tried since is to store each of the filters in a variable as plain text, with a simple check for the query string being there, but it doesn't seem to work. Any help would be much appreciated.
Ideally, you'd perform all the filtering in a custom extension - XSLT was really not made for these things; however - here's how I usually handle this (common problem):
<!-- Grab all resources -->
<xsl:variable name="allResources" select="$resourcesRoot//Resources" />
<xsl:variable name="resourcesFilteredByCountries" select="$allResources[umb:Split(countries, ',')/value = $CountriesSplit]" />
<xsl:variable name="resourcesFilteredByYear" select="$allResources[year = $YearsSplit]" />
<!-- We want to show only filtered resources (but all of them if no filter has been selected) -->
<xsl:variable name="resourcesToShow" select="$resourcesFilteredByYear | $resourcesFilteredByCountries | $allResources[not(normalize-space($CountriesSplit) and normalize-space($YearsSplit))]" />
<xsl:template match="/">
<!-- Pop some results in! -->
<xsl:apply-templates select="$resourcesToShow">
<xsl:sort select="year" order="descending" />
</xsl:apply-templates>
</xsl:template>
Hope that helps - shoot away with questions, if you have any :)
That looks great as a solution, but my only question would be is is scalable?
I have four filters so would I need to factor in a load of extra ors in there too? If there are 16 combinations rather than 3 (as in your example) I can see my brain turning to a thick sludge fairly quickly!
If this is going to be the simplest way, then many thanks for your help and I'll crack on with that!
That's why I mentioned a custom extension - even if you did the whole thing in Razor, you should handle the filtering in some custom code file that returned the results, so you don't do all the logic in the view. That said, I know that there are of course always reasons for not going that way :-)
Using all 4 of your filters, the result would look something like this:
<!-- Grab filter values -->
<xsl:variable name="CountriesSplit" select="umb:Split(umb:RequestQueryString('country'), ',')/value" />
<xsl:variable name="YearsSplit" select="umb:Split(umb:RequestQueryString('year'), ',')/value" />
<xsl:variable name="ThemesSplit" select="umb:Split(umb:RequestQueryString('theme'), ',')/value" />
<xsl:variable name="TypeSplit" select="umb:Split(umb:RequestQueryString('resourcetype'), ',')/value" />
<!-- Grab all resources -->
<xsl:variable name="allResources" select="$resourcesRoot//Resources" />
<!-- Create some filtered sets -->
<xsl:variable name="resourcesFilteredByCountries" select="$allResources[umb:Split(countries, ',')/value = $CountriesSplit]" />
<xsl:variable name="resourcesFilteredByYear" select="$allResources[year = $YearsSplit]" />
<xsl:variable name="resourcesFilteredByThemes" select="$allResources[theme = $ThemesSplit]" />
<xsl:variable name="resourcesFilteredByType" select="$allResources[resourcetype = $TypeSplit]" />
<!-- We want to show only filtered resources (but all of them if no filter has been selected) -->
<xsl:variable name="resourcesToShow" select="
$resourcesFilteredByYear |
$resourcesFilteredByCountries |
$resourcesFilteredByThemes |
$resourcesFilteredByType |
$allResources[not(
normalize-space($CountriesSplit) and
normalize-space($YearsSplit) and
normalize-space($ThemesSplit) and
normalize-space($TypeSplit)
)]"
/>
<xsl:template match="/">
<!-- Pop some results in! -->
<xsl:apply-templates select="$resourcesToShow">
<xsl:sort select="year" order="descending" />
</xsl:apply-templates>
</xsl:template>
Ideally, you just want this:
<xsl:variable name="resourcesToShow" select="tom:GetFilteredResources()" />
<xsl:template match="/">
<!-- Pop some results in! -->
<xsl:apply-templates select="$resourcesToShow/Resources">
<xsl:sort select="year" order="descending" />
</xsl:apply-templates>
</xsl:template>
- and not have your view be concerned about how to get those resources, right? :-)
That looks suspiciously simple.. o_O I'll give it a whirl tomorrow.
So from a logic point of view what happens if I pick a couple of items from Themes a couple from Countries and then nothing from Years and Type?
The second solution looks great too. Have you got some further reading on setting up a custom extension? I'm always looking to find a neater way to do things
All of the variables that selects nodes are holding sets of nodes.
We start by selecting all applicable resources (the $allResources variable).
Then we create additional subsets of those by using the various filters. A set can contain a truckload of nodes or none - so when we combine all the variables (using the pipe character) we get a new set of all the filtered nodes. Here's the great part: A node can only exist once in a set - so even if the same node gets selected multiple times (e.g. both by year and by country), it'll only be present once in the combined set.
To handle the "what if no filters are used" scenario we do one more thing - we add the $allResources set to the filtered nodes - but only if none of the filters were used.
I have a resource in the system and I know it is the only one where these four values exist. If I tick all the boxes for each of these values and hit search, I somehow get everything back..?
I probably didn't explain enough at the start. As they are tick boxes, I want use these to refine the results that come back, rather than expand them. So an example query run against the allResources variable should be:
[Country 1 | Country 2] and [2012 | 2013] and [Theme1] and [Any Type]
or with a different selection
[Any Country] and [2012 | 2013 | 2014] and [Any Theme] and [Type 3]
or when the page loads
[Any Country] and [Any Year] and [Any Theme] and [Any Type]
The problem that I was having was that the and parts were too rigid and if a value didn't exist in the query string, I wanted to exclude it completely from the query.
Testing querystrings
Hey guys,
I'm building a page that has some filter boxes on (check boxes). There are four sets of filters.
On my page I have a macro that reads in the values of query strings sent to the page and then displays the results accordingly. I have got this to work just fine using a set of and statements in an apply-template.
The problem I now have though is that it is too precise. To get a result back, I have to to check everything and then uncheck the one box I don't want to filter on (because of the and filters). It also means that when the page loads, I get no results and I would like to load everything by default.
My ideal situation would be to test to see if the query string exists, if it does include it in the filter. If it doesn't exist, don't filter on it. I could probably do this with 16 seperate if statements where I pop a different apply-tempate in each one, but that sounds ridiculous to me.
My original code is below (I have only included two filters to keep the code to a minimum on here). What I have tried since is to store each of the filters in a variable as plain text, with a simple check for the query string being there, but it doesn't seem to work. Any help would be much appreciated.
Thanks,
Tom
To pass values as a parameter, I have tried doing the following:
Hi Tom,
Ideally, you'd perform all the filtering in a custom extension - XSLT was really not made for these things; however - here's how I usually handle this (common problem):
Hope that helps - shoot away with questions, if you have any :)
/Chriztian
That looks great as a solution, but my only question would be is is scalable?
I have four filters so would I need to factor in a load of extra ors in there too? If there are 16 combinations rather than 3 (as in your example) I can see my brain turning to a thick sludge fairly quickly!
If this is going to be the simplest way, then many thanks for your help and I'll crack on with that!
Just out of interest, what would be a more suitable tool for doing things like this? Razor?
T
Hi Tom,
That's why I mentioned a custom extension - even if you did the whole thing in Razor, you should handle the filtering in some custom code file that returned the results, so you don't do all the logic in the view. That said, I know that there are of course always reasons for not going that way :-)
Using all 4 of your filters, the result would look something like this:
Ideally, you just want this:
- and not have your view be concerned about how to get those resources, right? :-)
/Chriztian
That looks suspiciously simple.. o_O I'll give it a whirl tomorrow.
So from a logic point of view what happens if I pick a couple of items from Themes a couple from Countries and then nothing from Years and Type?
The second solution looks great too. Have you got some further reading on setting up a custom extension? I'm always looking to find a neater way to do things
Thanks a lot again for your help with this,
Tom
So this is the beauty of working with sets...
All of the variables that selects nodes are holding sets of nodes.
We start by selecting all applicable resources (the
$allResources
variable). Then we create additional subsets of those by using the various filters. A set can contain a truckload of nodes or none - so when we combine all the variables (using the pipe character) we get a new set of all the filtered nodes. Here's the great part: A node can only exist once in a set - so even if the same node gets selected multiple times (e.g. both by year and by country), it'll only be present once in the combined set.To handle the "what if no filters are used" scenario we do one more thing - we add the
$allResources
set to the filtered nodes - but only if none of the filters were used.RE: extensions: I've learned most by looking at the source for some of the existing ones in umbraco.library - e.g., the Split() method.
/Chriztian
I think the pipes are making it too vague now. :(
I have a resource in the system and I know it is the only one where these four values exist. If I tick all the boxes for each of these values and hit search, I somehow get everything back..?
I probably didn't explain enough at the start. As they are tick boxes, I want use these to refine the results that come back, rather than expand them. So an example query run against the allResources variable should be:
[Country 1 | Country 2] and [2012 | 2013] and [Theme1] and [Any Type]
or with a different selection
[Any Country] and [2012 | 2013 | 2014] and [Any Theme] and [Type 3]
or when the page loads
[Any Country] and [Any Year] and [Any Theme] and [Any Type]
The problem that I was having was that the and parts were too rigid and if a value didn't exist in the query string, I wanted to exclude it completely from the query.
Hi again,
Hehe - now you're really getting far down the rabbit hole :)
You can do that by adding the fallback to each of the filtered sets, and then picking all the nodes that exist in all sets, like this:
But again: Doing this with an extension would be much better... (I know, repeating myself :-)
/Chriztian
Perfect as always. I can't believe how simple that is as a code snippet.
Definitely reusing that one again!
is working on a reply...