Re: [xsl] Noob: hmm using nested templates, how do i get the nodes within

Subject: Re: [xsl] Noob: hmm using nested templates, how do i get the nodes within
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 18 Sep 2003 13:07:38 -0400
Barry,

Two problems here. One is the one you asked about ... how you pass a node to a named template as a parameter. The other is that either you haven't quite understood the notion of a "context node" (a critical notion in XSLT), or you haven't explained/described your problem (since it doesn't match your approach).

The second problem is the deeper one, and if we address it the first one will just go away.

At 04:38 AM 9/18/2003, you wrote:
so now i heard i could use nested template, like
-------------------------------------------------
<xsl:template match="//Row">
<fo:table-row>
<fo:table-cell border="solid black 1 px" border-collapse="collapse">
<fo:block>
<xsl:value-of select="TT"/>
</fo:block>
</fo:table-cell>
<xsl:call-template name="cell"/>
</fo:table-row>
</xsl:template>

Correct. Calling the "cell" template with no parameters, it nonetheless takes the context node as its context node. So if in your source data you have


<Row>
  <aaa>Total</aaa>
  <TdD>null</TdD>
  <NA>45678</NA>
  <FR>45644</FR>
</Row>

when you evaluate this (or any) Row, the template above gives you an fo:table-row containing a cell with the value of the Row's TT child (hey! you don't have any! so what do you want in that cell?), followed by whatever the "cell" template does, with the matched node of the calling template (the row) as its context node.

But when you call the "cell" template

<xsl:template name="cell">
<fo:table-cell border="solid black 1 px" border-collapse="collapse">
<fo:block>
<xsl:variable name="cell" select="//Row/[*]"/> <---------------Here's where i think i go wrong, how do i select the nodes

Right -- you're stuck. How, from a "Row" (the context node) do you pick each one of the nodes in turn?


Actually, it can be done, wrapping it all in a for-each -- but this technique isn't actually the most efficient way XSLT offers to address your requirement.

Better would be not to pick these nodes up from the Row, but to *change* the context node to each of these. This is very simply done by applying its own template to each them, using a match. Then the context node changes by itself:

<xsl:template match="TdD | NA | FR">
  <fo:table-cell border="solid black 1 px" border-collapse="collapse">
    <fo:block>
    <!-- we don't need to bind any nodes to the variable: rather, the
         node we're interested in is whatever node we matched:
         our context node -->
      <xsl:choose>
        <xsl:when test="not(self::node() = 'null')">
           <xsl:value-of select="self::node()"/>
        </xsl:when>
      </xsl:choose>
    </fo:block>
  </fo:table-cell>
</xsl:template>

Which could be simplified and abbreviated to

<xsl:template match="TdD | NA | FR">
  <fo:table-cell border="solid black 1 px" border-collapse="collapse">
    <fo:block>
      <xsl:value-of select="self::node()[not(.='null')]"/>
    </fo:block>
  </fo:table-cell>
</xsl:template>

But to call this template for each of those nodes, we need to change our calling template:

<xsl:template match="Row">
  <fo:table-row>
    <fo:table-cell border="solid black 1 px" border-collapse="collapse">
      <fo:block>
        <xsl:value-of select="child::TT"/>
        <!-- whatever that is... I think you mean select="aaa" -->
      </fo:block>
    </fo:table-cell>
    <xsl:apply-templates name="child::TdD | child::NA | child::FR"/>
  </fo:table-row>
</xsl:template>

"TdD | NA | FR" is short for "child::TdD | child::NA | child::FR", just to be perfectly clear.

Barry, I suggest you might spend a bit of time researching the XSLT processing model. Basically an XSLT processor starts by matching a root node, and is set up by default to traverse down through the tree (depth-first traversal in "document order"), matching templates as it goes. Each of these templates has the opportunity to create nodes in the output, nodes which in each case end up "wrapping" or "containing" the nodes generated by templates "lower" in the tree (its children and descendants). You simply need to match at two levels -- the row (which generates a row, surprisingly) and the item within the row (which generates a cell). The XSLT Processor can take care of this traversal for you, if you let it.

Note that although this is "natural", there are times when other complications -- such as, one might want two of your three nodes grouped together in a single cell -- make it very useful to do it with a named template after all. Such as

<xsl:template match="Row">
  <fo:table-row>
    <fo:table-cell border="solid black 1 px" border-collapse="collapse">
      <fo:block>
        <xsl:value-of select="child::TT"/>
        <!-- whatever that is... I think you mean select="aaa" -->
      </fo:block>
    </fo:table-cell>
    <xsl:call-template name="cell">
      <xsl:with-param name="contents" select="TdD | NA"/>
    </xsl:call-template>
    <xsl:call-template name="cell">
      <xsl:with-param name="contents" select="FR"/>
    </xsl:call-template>
  </fo:table-row>
</xsl:template>

<xsl:template name="cell">
  <xsl:param name="contents" select="."/>
  <xsl:variable name="string-value">
    <xsl:apply-templates select="contents"/>
  </xsl:variable>
  <fo:table-cell border="solid black 1 px" border-collapse="collapse">
    <xsl:for-each select="$contents[not(.='null')">
      <fo:block>
        <xsl:value-of select="."/>
      </fo:block>
    </xsl:for-each>
  </fo:table-cell>
</xsl:template>

I hope this is helpful. If it's confusing, study up on templates and the processing model.

Cheers,
Wendell






Any more suggestions.


XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list

___&&__&_&___&_&__&&&__&_&__&__&&____&&_&___&__&_&&_____&__&__&&_____&_&&_ "Thus I make my own use of the telegraph, without consulting the directors, like the sparrows, which I perceive use it extensively for a perch." -- Thoreau


XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list



Current Thread