Copied to clipboard

Flag this post as spam?

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


  • Eran 292 posts 436 karma points
    Dec 21, 2010 @ 03:11
    Eran
    0

    loop on repeatable custom content - simple question

    hi. i have this xmldump from my web site:
    (the site is 4.5 schema)

    <projectDetail id="1081" level="3" nodeName="example-1" urlName="example-1"> 
    <projectName>example</projectName>
      <externalLinks>
        <items>
          <item>
            <data alias="ExtLinkTxt"><![CDATA[link to site]]></data>
            <data alias="ExtLinkUrl"><![CDATA[http://www.natalkids.org.uk/]]></data>
            <data alias="ExtLinkToolTip"><![CDATA[this is tooltip]]></data>
          </item>
        </items>
      </externalLinks>
    </projectDetail>

    projectDetail is my current page.
    the externalLinks node is created by the repeatable custom content datatype.
    (note that the package generates old schema xml).

    i worked before with this datatype without any problem, but from some reason i'm not succeded to loop on this.

    i tried this and few other options without success. where i am wrong?

     

    <xsl:if test="$externalLink/items/item">  
    <xsl:for-each select="$externalLink/items/item">
      <h2>Repeatable Custom Content: Item <xsl:value-of select="position()"/></h2>
           <ul>
              <li>Property 1 value: <xsl:value-of select="./data [@alias = 'ExtLinkTxt']" disable-output-escaping="yes"/></li>
              <li>Property 2 value: <xsl:value-of select="./data [@alias = 'ExtLinkUrl']" disable-output-escaping="yes"/>
              </li><li>Property 3 value: <xsl:value-of select="./data [@alias = 'ExtLinkToolTip']" disable-output-escaping="yes"/></li>
            </ul>
      </xsl:for-each>
    </xsl:if>

     

  • Tom Fulton 2030 posts 4998 karma points c-trib
    Dec 21, 2010 @ 03:58
    Tom Fulton
    0

    Hi,

    First of all, I don't think Repeatable Custom Content is compatible with Umbraco 4.5.  Not sure to what extent, but since you've got the XML there I guess it should work.

    What is happening, an error on saving or its just not outputting anything?

    How are you setting the $externalLink variable?

  • Eran 292 posts 436 karma points
    Dec 21, 2010 @ 04:28
    Eran
    0

    ok, so now when i'm not using template, the loop is working, like this:

    <xsl:for-each select="$currentPage/externalLinks/items/item">

     

    but i dont get it working with template. (how i send it to template? do i need to use msxml:node-set in the template?

    Thanks,

    Eran.

  • Thijs Kuipers 43 posts 66 karma points
    Dec 22, 2010 @ 12:48
    Thijs Kuipers
    0

    What is $externalLink? Have you checked whether there's anything in that variable/parameter using e.g. <xsl:copy-of/> ? It's the only point of failure I can see.

  • Eran 292 posts 436 karma points
    Dec 22, 2010 @ 13:04
    Eran
    0

    hi,
    the $externalLink contains this xml:

     <items>
         
    <item>
           
    <data alias="ExtLinkTxt"><![CDATA[link to site]]></data>
           
    <data alias="ExtLinkUrl"><![CDATA[http://www.natalkids.org.uk/]]></data>
           
    <data alias="ExtLinkToolTip"><![CDATA[this is tooltip]]></data>
         
    </item>
       
    </items>

    like i said, the above loop is working when i excute it in the main template (match=/) but i want to send it to different template (in the same macro).
    i can't understand why its not working. i tried few options with zero results.

    thanks,

    Eran.

     

    
    
    

     

  • Thijs Kuipers 43 posts 66 karma points
    Dec 22, 2010 @ 13:23
    Thijs Kuipers
    0

    In what scope is the $externalLink variable? Is it in the global stylesheet itself, or do you create it in the <template match="/"> ?

    Variables created within templates are only available (scoped) within that template. If you don't want to or can't have the $externalLink in the global scope of the stylesheet, you could use a template with a parameter and call the template with $externalLink as input parameter.

    From my TextMate snippets, template definition:

    <xsl:template name="QName" mode="QName">
        <xsl:param name="QName" select="Expr"/>
    </xsl:template>

    Call template with parameter:

    <xsl:call-template name="QName">
        <xsl:with-param name="QName" select="Expr"/>
    </xsl:call-template>
  • Eran 292 posts 436 karma points
    Dec 22, 2010 @ 14:44
    Eran
    0

    my question is exactly about that - how to send this variable to template.
    i did it before a lot of times. but from some reason i dont succeeded to do that now.
    i attached here the whole macro code so you can understand the while picture:

    <xsl:param name="currentPage"/>
     

      <xsl:template match="/">
      <div class="entry">
        <!-- Working -->
          <xsl:if test="$currentPage/externalLinks">
            <xsl:for-each select="$currentPage/externalLinks/items/item">
              <xsl:element name="a">
                <xsl:attribute name="href">
                  <xsl:value-of select="./data [@alias = 'ExtLinkUrl']" disable-output-escaping="yes"/>
                </xsl:attribute>
              </xsl:element>
              <span>|</span>
            </xsl:for-each>
          </xsl:if>

        <!-- not Working -->
        <xsl:call-template name="showExternalLinks">
            <xsl:with-param name="externalLink">
              <xsl:copy-of select="$currentPage/externalLink/items"/>
            </xsl:with-param>
          </xsl:call-template>
               
      </div>
    </xsl:template>
     

      <xsl:template name="showExternalLinks">
        <xsl:param name="externalLink" />  
          <xsl:for-each select="msxml:node-set($externalLink/items/item)">
            <xsl:element name="a">
              <xsl:attribute name="href">
                <xsl:value-of select="./data [@alias = 'ExtLinkUrl']" disable-output-escaping="yes"/>
              </xsl:attribute>
            </xsl:element>
            <span>|</span>
          </xsl:for-each> 
      </xsl:template>

    </xsl:stylesheet>
  • Tom Fulton 2030 posts 4998 karma points c-trib
    Dec 22, 2010 @ 14:52
    Tom Fulton
    0

    Hi Eran,

    This should work, but I see a possible typo in the <!-- not Working --> section

    $currentPage/externalLink should be $currentPage/externalLinks (plural), I believe

    Also, I think you should leave off /items and just pass it like so:

    <xsl:copy-of select="$currentPage/externalLinks"/>

    Then in your for-each loop inside the showExternalLinks template, I think you should change to:

    <xsl:for-each select="msxml:node-set($externalLink)/items/item">

    If not, to help debug, write out the following in your template so you can see the XML that's getting passed and that should help figure out what needs to be changed:

      <xsl:template name="showExternalLinks">
       
    <xsl:param name="externalLink" />  
    <!-- debug -->
    <textarea><xsl:copy-of select="$externalLink"/></textarea>

     

  • Thijs Kuipers 43 posts 66 karma points
    Dec 22, 2010 @ 14:57
    Thijs Kuipers
    0

    Well:

    First: why do you use copy-of instead of value-of? When you use value-of, you won't need to use msxml:node-set(). Don't double the trouble.

    Second: in your current copy-of, you're selecting externalLink instead of externalLinks. Seem like a typo.

    Third: in your showExternalLinks template, your externalLink doesn't contain "/items/item" nodes when you've called it by passing in "/externalLink/items". It will contain only "/item" nodes.

    You might want to consider using a named template that matches "externalLinks", since in the new schema it actually has that name. Then it would become something like

    <xsl:apply-templates select="$currentPage/externalLinks"/>

    Combined with a template

    <xsl:template match="externalLinks">

    that does the plumbing (like looping over the items).

  • Eran 292 posts 436 karma points
    Dec 22, 2010 @ 16:30
    Eran
    0

    tom and thijs: i combine your answers with few modifications, and now i have a working solution:
    in the regular method (me and tom method), i call the template like this:

            <xsl:call-template name="showExternalLinks">
            <xsl:with-param name="externalLinks">
              <xsl:copy-of select="$currentPage/externalLinks"/>
            </xsl:with-param>
          </xsl:call-template>

    and the template looks like:

      <xsl:template name="showExternalLinks">
        <xsl:param name="externalLinks" />
          <xsl:for-each select="msxml:node-set($externalLinks)//items/item">
            <xsl:element name="a">
              <xsl:attribute name="href">
                <xsl:value-of select="./data [@alias = 'ExtLinkUrl']" disable-output-escaping="yes"/>
              </xsl:attribute>
          </xsl:for-each>
      </xsl:template>

    this is working perfectlly and my problem is solved - thanks!

    just for interest and learning - thijs: your method is working, but with one problem: its not looping on all the items.
    i wonder how can i solve this:
    i call the template like:

     <xsl:apply-templates select="$currentPage/externalLinks"/>

    and the template looks like:

      <xsl:template match="externalLinks">
        <xsl:element name="a">
          <xsl:attribute name="href">
            <xsl:value-of select="./items/item/data [@alias = 'ExtLinkUrl']" disable-output-escaping="yes"/>
          </xsl:attribute>
      </xsl:template>

    like i said, the problem is that its only display one item, and not all of them.



  • Thijs Kuipers 43 posts 66 karma points
    Dec 22, 2010 @ 16:40
    Thijs Kuipers
    0

    Let me be nice and post the full code of what I meant ;)

    <xsl:template match="/">
        <xsl:apply-templates select="$currentPage/externalLinks"/>
    </xsl:template>
    
    <xsl:template match="externalLinks">
        <xsl:for-each select="./items/item">
            <a>
                <xsl:attribute name="href">
                    <xsl:value-of select="./data [@alias = 'ExtLinkUrl']" disable-output-escaping="yes"/>
                </xsl:attribute>
            </a>
        </xsl:for-each>
    </xsl:template>

    You will still need to loop over the "/items/item" nodes at some point. It is just a matter of taste at which point you choose to do it, I like keeping my root template clean, so I can break up the XSLT in smaller "problems".

    One other (even cleaner) solution would be:

    <xsl:template match="/">
        <xsl:apply-templates select="$currentPage/externalLinks/items/item"/>
    </xsl:template>
    
    <xsl:template match="externalLinks/items/item">
        <a>
            <xsl:attribute name="href">
                <xsl:value-of select="./data [@alias = 'ExtLinkUrl']" disable-output-escaping="yes"/>
            </xsl:attribute>
        </a>
    </xsl:template>

    Yeah, looking back at it the latter might be the best solution.

  • Eran 292 posts 436 karma points
    Dec 22, 2010 @ 16:54
    Eran
    0

    oright - i tried to do like in your second method - without the for-each loop.

    by the way, is the method with template match is more efficient than the "regular" one?

    Thanks.

  • Thijs Kuipers 43 posts 66 karma points
    Dec 22, 2010 @ 17:04
    Thijs Kuipers
    0

    If you mean more efficient CPU or memory-wise, I wouldn't know. Never tested it.

    However, I find it to be more efficient in writing. This time, you know what you're selecting on beforehand (namely "externalLinks/items/item"). So you know the name and type of each node. However, you might encounter a situation where you select a more general set of nodes. Using "apply-templates" combined with "template match='nodeSelector'" you're able to write concise and general XSLT at the same time. I allows you to implicitly include and exclude certain node types from the selected set.

    E.g. I've just implemented Examine (search engine provider) on a 4.5 site. The resulting nodeset for a query could point to any node type included in the Examine index. Using a combination of matching and named templates, I was able to list those results in HTML, with a minimum amount of effort. That's efficient.

  • Eran 292 posts 436 karma points
    Dec 22, 2010 @ 17:17
    Eran
    0

    thanks for your time and efforts. i marketd your answer as the solution, as you supply working solution to my problem.
    thanks for all the other that tried to help.

    p.s - thikjs, it will be interesting how you implements the examine search. i think that we need more real world examples/posts for examine + umbraco.
    i wrote about my experience in my blog.

    regards,
    Eran.

  • Thijs Kuipers 43 posts 66 karma points
    Dec 22, 2010 @ 18:26
    Thijs Kuipers
    0

    Regarding Examine: I'm using the XSLT Extensions in Examine 1.0

    So in config/xsltExtensions.config you'll need to register the Examine XSLT extensions:

    <XsltExtensions>
      <ext assembly="UmbracoExamine" type="UmbracoExamine.XsltExtensions" alias="Examine"/>
    </XsltExtensions>

    Next I created an XSLT macro with bits like:

    <xsl:variable name="rootNode" select="$currentPage/ancestor::root"/>
    <xsl:variable name="searchQuery" select="umbraco.library:RequestQueryString('s')"/>
    
    <xsl template match="/">
      <!-- This is the actual search query in Examine.
           First argument is (string)searchText,
           second is (bool)useWildCards,
           third is (string)providerName 
           Multiple overloads of the same method exist, so look at the documentation. -->
      <xsl:variable name="searchResults" select="Examine:Search($searchQuery, true(), 'SiteSearchSearcher')"/>
      <xsl:apply-templates select="$searchResults/nodes/node">
        <xsl:sort select="@score" data-type="number" order="descending" />
      </xsl:apply-templates>
    </xsl:template>
    
    <!-- Template used to match nodes that have an @score attribute,
         and thus come from Examine results -->
    <xsl:template match="node[@score]">
      <!-- Get the actual node by id -->
      <xsl:variable name="currentNode" select="$rootNode/descendant::*[@id = current()/@id]"/>
      <!-- Call named template for the actual node, wrapped in for-each just in case
           a node appears twice for an id -->
      <xsl:for-each select="$currentNode">
        <xsl:call-template name="Post"/>
      </xsl:for-each>
    </xsl:template>
    
    <xsl:template name="Post">
      <!-- Create some (X)HTML markup from the current node -->
    </xsl:template>

    In this case, I'm using a named template (because all searchable pages inherit from the same Document Type), but had it been the case that nodes from multiple document types could be in the search result nodeset, I would have created multiple matching templates.

  • Eran 292 posts 436 karma points
    Dec 22, 2010 @ 18:35
    Eran
    0

    very interesting, thank for sharing. i will search for the ducumantation for the xslt and examine.

    thanks!

  • Thijs Kuipers 43 posts 66 karma points
    Dec 22, 2010 @ 19:26
    Thijs Kuipers
    0

    I don't know whether there's a lot of documentation yet, but take a look at this file at Codeplex, it's the source of the Examine XSLT extensions with comments.

    http://examine.codeplex.com/SourceControl/changeset/view/57954#1074649

  • Eran 292 posts 436 karma points
    Dec 22, 2010 @ 19:34
    Eran
    0

    that will save me time, thanks!

    Eran

Please Sign in or register to post replies

Write your reply to:

Draft