Copied to clipboard

Flag this post as spam?

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


  • trfletch 598 posts 604 karma points
    Jan 27, 2010 @ 16:37
    trfletch
    0

    Search based on dropdowns using XSLT

    Hi, I have an umbraco V4 website that has the following structure:

    Jobs
       -Categories
              -Cat 1
                      -Job 1
                      -Job 2
                      -Job 3
              -Cat 2
                      -Job 4
                      -Job 5
    Locations
       - London
       - South East
       - North

    I have created an ultimate picker dropdown list that is populated by the locations node and this is a property of the Job document type, therefore Job 1 is in Category 1 and may have London selected as the location.

    I want to be able to do a search using two drop down lists based on the category and the location. I have so far created the XSLT below which creates the two drop down lists but I am unsure as to where I go from here and whether what I am trying to achieve is going to be possible using XSLT. Can anyone give me a nudge in the right direction? I want there to be a Search button that the using clicks to search after selecting the options in the two drop downs and I want the results to be displayed on a different page. Many thanks. This is the XSLT I have so far, as you can see it is not much!

    <select name="jobcategory">
     <xsl:for-each select="umbraco.library:GetXmlNodeById(1117)//node">
      <option>
       <xsl:attribute name="value">
         <xsl:value-of select="./@id"/>
       </xsl:attribute>
       <xsl:value-of select="./@nodeName"/>
      </option>
     </xsl:for-each>
    </select>
    <br/>


    <select name="location">
     <xsl:for-each select="umbraco.library:GetXmlNodeById(1134)//node">
      <option>
       <xsl:attribute name="value">
         <xsl:value-of select="./@id"/>
       </xsl:attribute>
       <xsl:value-of select="./@nodeName"/>
      </option>
     </xsl:for-each>
    </select>
  • Jesper Ordrup 1019 posts 1528 karma points MVP
    Jan 27, 2010 @ 16:53
    Jesper Ordrup
    1

    Hi,

    It's absolutely doable. I created something like that. A year a go I implemented this:

    I think I used xsltsearch as a starter but I could be wrong there. But it's xslt all the way. 

    I'll check if I have the source if you're interested?

    /Jesper Ordrup

  • dandrayne 1138 posts 2262 karma points
    Jan 27, 2010 @ 16:59
    dandrayne
    1

    +1 on the totally doable in xslt (with some wrestling) - we did http://www.haddenrankin.com/sell-with-us.aspx for searching properties - the properties come in via an xml file and we search using only xslt.

    Basically all you're doing is creating a form which sends to a new page, then using umbraco.library:Request to grab the querystring values and use them as filters in the xslt, much as you do for e.g. "umbracoNaviHide = '1'" etc.

     

  • trfletch 598 posts 604 karma points
    Jan 27, 2010 @ 17:07
    trfletch
    0

    Thanks for the replies, that's good news to hear that it can be done, the bad news is that I'm stuck as to how I do it, I have never used umbraco.library:request before and do not have a lot of experience with forms either :-(

  • dandrayne 1138 posts 2262 karma points
    Jan 27, 2010 @ 17:53
    dandrayne
    1

    OK here's a very quick rundown - any more would probably require a blog post over on geckonewmedia.com

    So, you have a dropdown with your categories and with your locations.  This is a good start.  Now, just wrap these in a form like

    <form action="/your_results_page.aspx" method="get">
    [Your Select boxes here, plus submit button]
    </form>

    Where the action is pointing to your results page.  Now on the template of your results page you'll need some xslt that requests the querystring and applies filters based on it.

    Such as on my property search example, I was wanting to check for "minimum rooms".  I set up an xslt variable then request the value from the querystring, supplying a fallback in case of errrors.

    <xsl:variable name="minrooms">
    <xsl:choose>
    <xsl:when test="umbraco.library:Request('minrooms') &lt;= 0 or string(umbraco.library:Request('minrooms')) = '' or string(umbraco.library:Request('minrooms')) = 'NaN'">0</xsl:when>
    <xsl:otherwise>
    <xsl:value-of select="umbraco.library:Request('minrooms')"/>
    </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>

    This just looks for a querystring value of the format

    www.whatever.com/page.aspx?minrooms=2

    and applies it to an xslt variable.  Now we have the variable we can apply a filter to our for-each

    <xsl:for-each select="$feed/properties/property [./Bedrooms &gt;= $minrooms]"/>
    </xsl:for-each>

    Just like a normal umbraco loop (except above the "Bedrooms" label is a node, not an attribute as in umbraco. The above is a simple example - the full for-each looked more like

    <xsl:for-each select="$feed/RegistrationExtraction/RegistrationList/Registration  [contains(Exslt.ExsltStrings:lowercase(./Area), Exslt.ExsltStrings:lowercase($area)) and (./RegistrationType = 'Sale')  and (./AskingPrice &lt; $maxprice) and (./Bedrooms &gt;= $minrooms)]"/>

    You can see how it builds in size over time, but essentially theres a lot of repeating fundamental concepts that you'll already be used to from working with umbraco for a little while.

    Also, if you're having trouble with the form you can test your results by just modifying the querystring manually in the browser.

    Dan

    p.s. another thing I find useful is to enable a "debug" option in the xslt, just create a variable and switch it from 1 to 0 or whatever, then you could have something like below - just printing out the values of all your variables to make sure everything is working as needed.

    <xsl:if test="$debug = 1">
    <div style="color: #000;border:1px solid red;color: #000;padding: 10px; background: yellow">
    <strong>Sorted - </strong><xsl:value-of select="$sort" /> -
    <strong>max rent - </strong><xsl:value-of select="$maxprice" /> -
    <strong>Area - </strong><xsl:value-of select="$area" /> -
    <strong>Min Rooms - </strong><xsl:value-of select="$minrooms" /> -
    <strong>Found - </strong><xsl:value-of select="$numberOfRecords" /> -
    <strong>CurrentPage = </strong><xsl:value-of select="$pageNumber" />
    </div>
    </xsl:if>

    Hope this starts you on the right direction!

    Dan

  • trfletch 598 posts 604 karma points
    Jan 28, 2010 @ 11:30
    trfletch
    0

    Hi Dan,

    Many thanks, that has got me started but already I am a little stuck. I have left the form for now and will deal with that later but I have got the following XSLT for my results page but I am not sure as to how I modify my for-each to check for the category and location. The nodes I am looking for are child nodes of Categorie folders so my first bit works fine, if the query string category equals the node ID of a category then all the child nodes are listed, I now want it to be able to find the child nodes if they have a property type that is equal to the location query string (the location property type is an ultimate picker so it should be node ID I believe). I'm assuming I need some sort of "and" command but my XSLT knowledge is letting me down!! This is what I have so far:

    <xsl:variable name="category">
    <xsl:choose>
            <xsl:when test="umbraco.library:Request('category') &lt;= 0 or string(umbraco.library:Request('category')) = '' or string(umbraco.library:Request('category')) = 'NaN'">0</xsl:when>
            <xsl:otherwise>
                    <xsl:value-of select="umbraco.library:Request('category')"/>
            </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:variable name="location">
    <xsl:choose>
            <xsl:when test="umbraco.library:Request('location') &lt;= 0 or string(umbraco.library:Request('location')) = '' or string(umbraco.library:Request('location')) = 'NaN'">0</xsl:when>
            <xsl:otherwise>
                    <xsl:value-of select="umbraco.library:Request('location')"/>
            </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>

    <xsl:for-each select="umbraco.library:GetXmlNodeById($category)/node [string(data [@alias='umbracoNaviHide']) != '1']">
     
      <a href="{umbraco.library:NiceUrl(@id)}">
       <xsl:value-of select="@nodeName"/>
      </a>
      
    </xsl:for-each>
  • dandrayne 1138 posts 2262 karma points
    Jan 28, 2010 @ 11:52
    dandrayne
    0

    What about this?

    <xsl:for-each select="umbraco.library:GetXmlNodeById($category)/node [string(data [@alias='umbracoNaviHide']) != '1' and string(data [@alias='location']) = $location]">

    Also might be worth sticking in something like

    <xsl:if test="count(umbraco.library:GetXmlNodeById($category)/node [string(data [@alias='umbracoNaviHide']) != '1' and string(data [@alias='location']) = $location]) = 0">
    <p>No results here</p>
    </xsl:if>

    Dan

  • trfletch 598 posts 604 karma points
    Jan 28, 2010 @ 13:05
    trfletch
    0

    Excellent, thanks once again Dan, that has been so much help. Think I have now got it cracked, just need to get my form XSLT setup to pass the query strings and sort out my styling. Just for anyone that is interested this is my XSLT for the search results page. Please let me know if you have any questions or if there is anything in there you think I could be doing a better way. This is the most complex XSLT I have written so far therefore I'm sure there will be errors or bad practices.

    <!-- GET CATEGORY QUERY STRING -->
    <xsl:variable name="category">
    <xsl:choose>
            <xsl:when test="umbraco.library:Request('category') &lt;= 0 or string(umbraco.library:Request('category')) = '' or string(umbraco.library:Request('category')) = 'NaN'">0</xsl:when>
            <xsl:otherwise>
                    <xsl:value-of select="umbraco.library:Request('category')"/>
            </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <!-- GET LOCATION QUERY STRING -->
    <xsl:variable name="location">
    <xsl:choose>
            <xsl:when test="umbraco.library:Request('location') &lt;= 0 or string(umbraco.library:Request('location')) = '' or string(umbraco.library:Request('location')) = 'NaN'">0</xsl:when>
            <xsl:otherwise>
                    <xsl:value-of select="umbraco.library:Request('location')"/>
            </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:choose>
    <!-- RESULTS WHEN CATEGORY IS ANY AND LOCATION IS SELECTED -->
    <xsl:when test="$category = 'any' and $location != 'any'">
    <xsl:for-each select="umbraco.library:GetXmlNodeById(1148)//node[@nodeTypeAlias='Job' and string(data [@alias='umbracoNaviHide']) != '1' and string(data [@alias='location']) = $location]">

      <a href="{umbraco.library:NiceUrl(@id)}">
       <xsl:value-of select="@nodeName"/>
      </a>
      
    </xsl:for-each>
    </xsl:when>
    <!-- RESULTS WHEN LOCATION IS ANY AND CATEGORY IS SELECTED -->
    <xsl:when test="$location = 'any' and $category != 'any'">
    <xsl:for-each select="umbraco.library:GetXmlNodeById($category)/node [string(data [@alias='umbracoNaviHide']) != '1']"> 
      
      <a href="{umbraco.library:NiceUrl(@id)}">
       <xsl:value-of select="@nodeName"/>
      </a>
      
    </xsl:for-each>
    </xsl:when>
    <!-- RESULTS WHEN CATEGORY AND LOCATION ARE BOTH ANY -->
    <xsl:when test="$category = 'any' and $location = 'any'">
            <xsl:for-each select="umbraco.library:GetXmlNodeById(1148)//node[@nodeTypeAlias='Job']">
      <a href="{umbraco.library:NiceUrl(@id)}">
       <xsl:value-of select="@nodeName"/>
      </a>
    </xsl:for-each>
    </xsl:when>
    <!-- RESULTS WHEN CATEGORY AND LOCATION ARE BOTH SELECTED -->
    <xsl:otherwise>
    <xsl:for-each select="umbraco.library:GetXmlNodeById($category)/node [string(data [@alias='umbracoNaviHide']) != '1' and string(data [@alias='location']) = $location]"> 
      
      <a href="{umbraco.library:NiceUrl(@id)}">
       <xsl:value-of select="@nodeName"/>
      </a>
      
    </xsl:for-each>
    </xsl:otherwise>
    </xsl:choose> 
  • trfletch 598 posts 604 karma points
    Jan 28, 2010 @ 13:22
    trfletch
    0

    Sorry, one more question. I have created my form but the problem is because it is within another form (the top level masterpage has a form that covers the whole page) it does not work. How can I get around this issue?

  • dandrayne 1138 posts 2262 karma points
    Jan 28, 2010 @ 13:56
    dandrayne
    0

    My firstport of call would be to determine if the runat=server form around your whole page is necessary.  I usually only use these directly around areas that need them, e.g. if doc2form is to be insterted in an editor field I'll wrap that in a form runat=server.  I think "canvas editing" requires a form, but I don't really use this much anyway.

    If the main form really is necessary, you'll need to use some javascript to submit the form.

  • trfletch 598 posts 604 karma points
    Jan 28, 2010 @ 14:27
    trfletch
    0

    I don't think it is, I was under the impression that all asp.net pages should be wrapped in a form that is why I always put a form directly after my body tag on the top level master. Is this not the case?

  • dandrayne 1138 posts 2262 karma points
    Jan 28, 2010 @ 15:18
    dandrayne
    0

    It's not strictly necessary unless you're using particular controls inside the page.  It's basically there as a asp.net bastardisation of html, injecting "statefulness" into a medium never designed for it. 

    I'd remove it and only re-add in circumstances where it is needed (doc2form, contour etc),

    Dan

     

  • trfletch 598 posts 604 karma points
    Jan 28, 2010 @ 16:56
    trfletch
    0

    Ok thanks, that is excellent. I have now got it working as I want. I have even created another template in the XSLT that shows the results as I want them and have set each "when" command to call that template rather than having to repeat all the CSS etc everytime. Once again thanks for all your help.

  • dandrayne 1138 posts 2262 karma points
    Jan 28, 2010 @ 17:32
    dandrayne
    0

    No problem at all, glad you got it sorted.  I'm still surprised by how much is possible in xslt!

  • Jake 23 posts 53 karma points
    Feb 19, 2010 @ 13:06
    Jake
    0

    Hi, did you get this all working ok? I am trying to do the same as we speak!

  • trfletch 598 posts 604 karma points
    Feb 19, 2010 @ 13:23
    trfletch
    0

    Yeah, what do you need to know?

  • Jake 23 posts 53 karma points
    Feb 19, 2010 @ 16:03
    Jake
    0

    I started trying to do this with xslt seach and got so far and got stuck. This looks like a more direct root and I wanted to see how you did it.

  • trfletch 598 posts 604 karma points
    Feb 19, 2010 @ 19:00
    trfletch
    0

    This is the XSLT for the dropdowns that I had on my homepage:

    <xsl:template match="/">
    <form action="/search-results.aspx" method="get">
    <div id="quicksearchdropdowns">
    <p>Job type</p>
    <select class="quicksearchdropdowns" name="category">
      <option value="any">Any</option>
     <xsl:for-each select="umbraco.library:GetXmlNodeById(1117)/node">
             <xsl:sort select="@nodeName" order="ascending" /> 
     
    <option>
       <xsl:attribute name="value">
         <xsl:value-of select="./@id"/>
       </xsl:attribute>
       <xsl:value-of select="./@nodeName"/>
      </option>
     </xsl:for-each>
    </select>
    <br/>
    <p>Location</p>
    <select class="quicksearchdropdowns" name="location">
      <option value="any">Any</option>
     <xsl:for-each select="umbraco.library:GetXmlNodeById(1134)/node">
             <xsl:sort select="@nodeName" order="ascending" /> 
      <option>
       <xsl:attribute name="value">
         <xsl:value-of select="./@id"/>
       </xsl:attribute>
       <xsl:value-of select="./@nodeName"/>
      </option>
     </xsl:for-each>
    </select>
    </div>
    <div id="quicksearchbutton">
    <input type="image" src="images/quicksearchbtn.jpg" alt="Submit button" />
    </div>
    </form>
    </xsl:template>

    Then this is the XSLT for my search results page that grabs the querystrings created using the above and then displays the nodes accordingly:

    <xsl:template match="/">

    <!-- GET CATEGORY QUERY STRING -->
    <xsl:variable name="categoryquery">
    <xsl:choose>
            <xsl:when test="umbraco.library:Request('category') &lt;= 0 or string(umbraco.library:Request('category')) = '' or string(umbraco.library:Request('category')) = 'NaN'">0</xsl:when>
            <xsl:otherwise>
                    <xsl:value-of select="umbraco.library:Request('category')"/>
            </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <!-- GET LOCATION QUERY STRING -->
    <xsl:variable name="locationquery">
    <xsl:choose>
            <xsl:when test="umbraco.library:Request('location') &lt;= 0 or string(umbraco.library:Request('location')) = '' or string(umbraco.library:Request('location')) = 'NaN'">0</xsl:when>
            <xsl:otherwise>
                    <xsl:value-of select="umbraco.library:Request('location')"/>
            </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:choose>
    <!-- RESULTS WHEN CATEGORY IS ANY AND LOCATION IS SELECTED -->
    <xsl:when test="$categoryquery = 'any' and $locationquery != 'any'">
    <xsl:for-each select="umbraco.library:GetXmlNodeById(1148)//node[@nodeTypeAlias='Job' and string(data [@alias='umbracoNaviHide']) != '1' and string(data [@alias='location']) = $locationquery]">
     <xsl:call-template name="displayResults">
     </xsl:call-template>
        
    </xsl:for-each>
    <xsl:if test="count(umbraco.library:GetXmlNodeById(1148)//node [@nodeTypeAlias='Job' and string(data [@alias='umbracoNaviHide']) != '1' and string(data [@alias='location']) = $locationquery]) = 0">
            <p>Your search returned no results</p>
     <br />
     <a href="/browse-all-jobs.aspx">Click here to browse all jobs</a>
    </xsl:if>
    </xsl:when>
    <!-- RESULTS WHEN LOCATION IS ANY AND CATEGORY IS SELECTED -->
    <xsl:when test="$locationquery = 'any' and $categoryquery != 'any'">
    <xsl:for-each select="umbraco.library:GetXmlNodeById($categoryquery)/node [string(data [@alias='umbracoNaviHide']) != '1']"> 
      
      <xsl:call-template name="displayResults">
      </xsl:call-template>
      
    </xsl:for-each>
    <xsl:if test="count(umbraco.library:GetXmlNodeById($categoryquery)/node [string(data [@alias='umbracoNaviHide']) != '1' ]) = 0">
              <p>Your search returned no results</p>
     <br />
     <a href="/browse-all-jobs.aspx">Click here to browse all jobs</a>
    </xsl:if>
    </xsl:when>
    <!-- RESULTS WHEN CATEGORY AND LOCATION ARE BOTH ANY -->
    <xsl:when test="$categoryquery = 'any' and $locationquery = 'any'">
            <xsl:for-each select="umbraco.library:GetXmlNodeById(1148)//node[@nodeTypeAlias='Job']">
     <xsl:call-template name="displayResults">
     </xsl:call-template>
      
    </xsl:for-each>

    </xsl:when>

    <!-- RESULTS WHEN CATEGORY AND LOCATION ARE BOTH SELECTED -->
    <xsl:otherwise>
    <xsl:for-each select="umbraco.library:GetXmlNodeById($categoryquery)/node [string(data [@alias='umbracoNaviHide']) != '1' and string(data [@alias='location']) = $locationquery]"> 
      
      <xsl:call-template name="displayResults">
      </xsl:call-template>
      
    </xsl:for-each>
    <xsl:if test="count(umbraco.library:GetXmlNodeById($categoryquery)/node [string(data [@alias='umbracoNaviHide']) != '1' and string(data [@alias='location']) = $locationquery]) = 0">
              <p>Your search returned no results</p>
     <br />
     <a href="/browse-all-jobs.aspx">Click here to browse all jobs</a>
    </xsl:if>
    </xsl:otherwise>
    </xsl:choose>
    </xsl:template>
    <xsl:template name="displayResults">
    <xsl:variable name="location" select="data [@alias = 'location']" />
               <xsl:variable name="recruiter" select="data [@alias = 'recruiter']" />
        <xsl:variable name="jobdescription" select="umbraco.library:StripHtml(data [@alias='jobdescription'])" />
      <div class="jobsearchresult">
      <div class="jobsearchresulttop">
      <xsl:comment><!-- --></xsl:comment>
      </div>
      <div class="jobsearchresultmiddle">
      <div class="jobsearchresultcontent">
      <div class="jobsearchresultleft">
      <h2><xsl:value-of select="@nodeName"/></h2>
      <h3><xsl:value-of select="umbraco.library:GetXmlNodeById($location)/@nodeName"/></h3>
      
      <xsl:value-of select="umbraco.library:TruncateString($jobdescription,200,'')"/>

         &nbsp;<a class="jobresultlink" href="{umbraco.library:NiceUrl(@id)}">Read more...
       </a>
      <div class="jobsearchresultsalary">
      Salary:&nbsp;<xsl:value-of select="data [@alias = 'salary']"/>
      </div>
      <div class="jobsearchresultdate">
      Closing date:
      <xsl:choose>
      <xsl:when test="data [@alias = 'closingdate'] !=''">
    <xsl:value-of select="umbraco.library:FormatDateTime(data [@alias = 'closingdate'], 'd MMM yyyy')"/>
      </xsl:when>
      <xsl:otherwise>
      No closing date
      </xsl:otherwise>
      </xsl:choose>
      </div>
      </div>
      <div class="jobsearchresultlogo">
     <img class="recruiterlogo">
          <xsl:attribute name="src">
              <xsl:value-of select="umbraco.library:GetMedia(umbraco.library:GetXmlNodeById($recruiter)/data  [@alias='recruiterlogo'], 'false')/data [@alias='umbracoFile']" />
          </xsl:attribute>
          <xsl:attribute name="width">
              <xsl:value-of select="umbraco.library:GetMedia(umbraco.library:GetXmlNodeById($recruiter)/data  [@alias='recruiterlogo'], 'false')/data [@alias='umbracoWidth']" />
          </xsl:attribute>
          <xsl:attribute name="height">
              <xsl:value-of select="umbraco.library:GetMedia(umbraco.library:GetXmlNodeById($recruiter)/data  [@alias='recruiterlogo'], 'false')/data [@alias='umbracoHeight']" />
          </xsl:attribute>
       <xsl:attribute name="alt">
      <xsl:value-of select="umbraco.library:GetXmlNodeById($recruiter)/@nodeName"/> logo</xsl:attribute>
          </img> 
      </div>
      </div>
      </div>
      
      <div class="jobsearchresultbottom">
      <xsl:comment><!-- --></xsl:comment>
      </div>
      </div>
    </xsl:template>

    Let me know if there are any bits that you do not understand or you want me to explain more.

  • Jake 23 posts 53 karma points
    Feb 19, 2010 @ 22:23
    Jake
    0

    Thanks for this, I am going to work through it this weekend

Please Sign in or register to post replies

Write your reply to:

Draft