Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • ianhoughton 281 posts 605 karma points c-trib
    Jun 08, 2010 @ 14:36
    ianhoughton
    0

    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:

    <xsl:template match="/">  <!-- start writing XSLT --> <div id="propertySearch">  <xsl:variable name="filterList" select="umbraco.library:GetPreValues('1089')"/> <xsl:for-each select="$filterList//preValue"> <input type="checkbox" name="filter" value="{@id}" /> <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" /> </div> </xsl:template>

    I have an existing page that lists ALL the properties using this XSLT:

    <div id="propertyListing">
    <ul>
    <xsl:for-each select="$currentPage/node [string(data [@alias='umbracoNaviHide']) != '1']">
        <li class="propertyWrapper">
            <div class="border"><div class="propertyNumber">Property No <xsl:value-of select="data [@alias = 'propertyNumber']"/></div></div>
            <xsl:choose>
                <xsl:when test="data [@alias = 'umbracoFile'] != ''">
                        <img src="{concat(substring-before(data [@alias='umbracoFile'],'.'), '_propertyThumb.jpg')}" alt="{data [@alias = 'photoText']}" width="295px" height="190px" class="right"></img>
                </xsl:when>
                <xsl:otherwise>
                        <img src="/css/images/blank.jpg" alt="blank image" width="295px" height="190px" class="right"></img>
                </xsl:otherwise>
                </xsl:choose>
            <h2><xsl:value-of select="data [@alias = 'propertyName']"/></h2>
            <xsl:if test="data [@alias = 'propertyType'] != ''">
                <p>Property Type: <em><xsl:value-of select="umbraco.library:GetPreValueAsString(data [@alias = 'propertyType'])"/></em></p>
            </xsl:if>
            <p>Facilities: <xsl:value-of select="data [@alias = 'facilities']"/></p>
            <p><a href="{umbraco.library:NiceUrl(@id)}">Property Information<!--<xsl:value-of select="@nodeName"/>--></a></p>
        </li>
        <div class="clearBoth"></div>
    </xsl:for-each>
    </ul>
    </div>

    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.

  • Peter Duncanson 430 posts 1360 karma points c-trib
    Jun 08, 2010 @ 17:07
    Peter Duncanson
    0

    Not 100% clear on what you are asking here? Can you give us a user case or similar? 

  • ianhoughton 281 posts 605 karma points c-trib
    Jun 08, 2010 @ 17:42
    ianhoughton
    0

    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.

     

     

     

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Jun 09, 2010 @ 15:41
    Matt Brailsford
    0

    This is how I've done it on a project of mine, it was geared towards artwork, but you could filter by criateria

    <

     

    xsl:variable name="artworkid" select="umbraco.library:RequestQueryString('artworkid')" />

    <

     

    xsl:variable name="collection" select="umbraco.library:RequestQueryString('collection')" />

    <

     

    xsl:variable name="category" select="umbraco.library:RequestQueryString('category')" />

    <

     

    xsl:variable name="subject" select="umbraco.library:RequestQueryString('subject')" />

    <

     

    xsl:variable name="orientation" select="umbraco.library:RequestQueryString('orientation')" />

    <

     

    xsl:variable name="rareorsoldout" select="umbraco.library:RequestQueryString('rareorsoldout')" />

    <

     

    xsl:variable name="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-each select="$nodes">
    ...
    </xsl:for-each>

    One of the XSLT gurus may have a better way, but it worked for me =)

    Matt

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Jun 09, 2010 @ 15:42
    Matt Brailsford
    0

    Man, you can never predict how this forum is going to format your code. Applogies for the weird spacing.

    Matt

  • ianhoughton 281 posts 605 karma points c-trib
    Jun 09, 2010 @ 17:38
    ianhoughton
    0

    Nearly there....

    This is the code on the search criteria page:

    <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:

    <xsl:param name="currentPage"/>
    
    <xsl:template match="/">
    
    <xsl:variable name="filterBy" select="umbraco.library:RequestQueryString('filter')" />
    
    <!-- TEMP: Print out list of filter ID's -->
    <xsl:if test="($filterBy) != ''">
    <xsl:value-of select="($filterBy)"/>
    </xsl:if>
    
    <xsl:variable name="nodes" select="$currentPage/node [contains($filterBy, data [@alias='facilities'])]" />
    
    <!-- The fun starts here -->
    <div id="propertyListing">
    <ul>
    <xsl:for-each select="$nodes">
    
    <!-- TEMP: Print out some logic -->
    AAA<xsl:value-of select="data [@alias = 'facilities']"/>AAA
    XXX<xsl:value-of select="$filterBy"/>XXX
    YYY<xsl:value-of select="contains($filterBy, data [@alias = 'facilities'])"/>YYY
    ZZZ<xsl:value-of select="not(contains($filterBy, data [@alias = 'facilities']))"/>ZZZ
    
        <li class="propertyWrapper">
            <div class="border"><div class="propertyNumber">Property No <xsl:value-of select="data [@alias = 'propertyNumber']"/></div></div>
            <xsl:choose>
                <xsl:when test="data [@alias = 'umbracoFile'] != ''">
                        <img src="{concat(substring-before(data [@alias='umbracoFile'],'.'), '_propertyThumb.jpg')}" alt="{data [@alias = 'photoText']}" width="295px" height="190px" class="right"></img>
                </xsl:when>
                <xsl:otherwise>
                        <img src="/css/images/blank.jpg" alt="blank image" width="295px" height="190px" class="right"></img>
                </xsl:otherwise>
                </xsl:choose>
            <h2><xsl:value-of select="data [@alias = 'propertyName']"/></h2>
            <xsl:if test="data [@alias = 'propertyType'] != ''">
                <p>Property Type: <em><xsl:value-of select="umbraco.library:GetPreValueAsString(data [@alias = 'propertyType'])"/></em></p>
            </xsl:if>
            <p>Facilities: <xsl:value-of select="data [@alias = 'facilities']"/></p>
            <p><a href="{umbraco.library:NiceUrl(@id)}">Property Information<!--<xsl:value-of select="@nodeName"/>--></a></p>
        </li>
        <div class="clearBoth"></div>
    </xsl:for-each>
    </ul>
    </div>
    </xsl:template>

    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 ?

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Jun 09, 2010 @ 21:58
    Matt Brailsford
    0

    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

  • ianhoughton 281 posts 605 karma points c-trib
    Jun 09, 2010 @ 23:55
    ianhoughton
    0

    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

    <xsl:variable name="searchterms">1369,1064</xsl:variable>
    <xsl:variable name="productattributes">1064,1372,1369</xsl:variable>
    <xsl:variable name="searchvalues" select="umbraco.library:Split($searchterms, ',')" />

    <xsl:for-each select="$searchvalues/value">
           
    <xsl:if test="contains(concat(',', $productattributes), concat(',', string(.), ','))">
                    Match
           
    </xsl:if>
    </xsl:for-each>

    but I can't seem to get it to work.

  • Josh Townson 67 posts 162 karma points
    Jun 10, 2010 @ 11:49
    Josh Townson
    0

    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.

    /Josh

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Jun 10, 2010 @ 15:06
    Matt Brailsford
    0

    Hey Ian,

    This worked for me (I added an additional comma on the end of the concat for $productattributes)

    <xsl:variable name="searchterms">1369,1064</xsl:variable>
    <xsl:variable name="productattributes">1064,1372,1369</xsl:variable>
    <xsl:variable name="searchvalues" select="umbraco.library:Split($searchterms, ',')" />
    <xsl:for-each select="$searchvalues/value">
            <xsl:if test="contains(concat(',', $productattributes, ','), concat(',', string(.), ','))">
                    Match
            </xsl:if>
    </xsl:for-each>

    Josh, I haven't tried that before, but that sounds cool, will definatly give it a go

    Matt

  • Josh Townson 67 posts 162 karma points
    Jun 10, 2010 @ 16:45
    Josh Townson
    0

    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

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Jun 10, 2010 @ 17:19
    Matt Brailsford
    0

    As a quick down and dirty AND check, you could do something like this

    <xsl:variable name="searchterms">1369,1064</xsl:variable>
    <xsl:variable name="productattributes">1064,1372,1369</xsl:variable>
    <xsl:variable name="searchvalues" select="umbraco.library:Split($searchterms, ',')" />
    <xsl:variable name="match">
       <xsl:for-each select="$searchvalues/value">
            <xsl:if test="contains(concat(',', $productattributes, ','), concat(',', string(.), ','))">1</xsl:if>
       </xsl:for-each>
    </xsl:variable>
    <xsl:if test="count($searchvalues/value) = string-length($match)">
        ...
    </xsl:if>

    Josh, would you recommend something like this then over the method you suggested?

    Matt

  • ianhoughton 281 posts 605 karma points c-trib
    Jun 10, 2010 @ 17:29
    ianhoughton
    0

    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.

  • ianhoughton 281 posts 605 karma points c-trib
    Jun 10, 2010 @ 17:31
    ianhoughton
    0

    Sorry, should have said, spoke to my client and an OR search would be OK.

  • Josh Townson 67 posts 162 karma points
    Jun 10, 2010 @ 17:34
    Josh Townson
    0

    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.

  • Josh Townson 67 posts 162 karma points
    Jun 10, 2010 @ 17:43
    Josh Townson
    0

    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

    <xsl:param name="currentPage"/>
    <xsl:variable name="filterBy" select="umbraco.library:RequestQueryString('filter')" />

    <xsl:template match="/">
    <ul>
    <xsl:for-each select="$currentPage/node">
    <xsl:variable name="propertyattributes" select="data [@alias = 'facilities']"/>
    <xsl:variable name="searchvalues" select="umbraco.library:Split($filterBy, ',')" />
    <xsl:variable name="match">
    <xsl:for-each select="$searchvalues/value">
    <xsl:if test="contains(concat(',', $productattributes, ','), 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>
    </ul>
    </xsl:template>

    For an OR match make the last test,

    <xsl:if test="string-length($match) &gt; 0">
    <li><xsl:value-of select="data [@alias = 'propertyName']"/></li>
    </xsl:if>
  • ianhoughton 281 posts 605 karma points c-trib
    Jun 10, 2010 @ 18:00
    ianhoughton
    0

    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.

  • Josh Townson 67 posts 162 karma points
    Jun 10, 2010 @ 18:39
    Josh Townson
    0

    sorry - copy paste mistake - it should have been.

      <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>

    teach me to try and cut small corners!

  • ianhoughton 281 posts 605 karma points c-trib
    Jun 10, 2010 @ 21:06
    ianhoughton
    0

    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.

  • fermat 4 posts 24 karma points
    Jan 06, 2013 @ 13:13
    fermat
    0

    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&#x00A0;
               
            </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.

Please Sign in or register to post replies

Write your reply to:

Draft