Re: [xsl] Changing Attributes Based on Ancestry

Subject: Re: [xsl] Changing Attributes Based on Ancestry
From: "Joerg Heinicke" <joerg.heinicke@xxxxxx>
Date: Tue, 9 Apr 2002 01:00:26 +0200
Hello Eric,

you have a few mistakes in your code.

>   <xsl:template name="top_bar">
>     <xsl:apply-templates select="document('./nav.xml')"/>
>   </xsl:template>
>
>   <xsl:template name="bottom_bar">
>     <xsl:apply-templates select="document('./nav.xml')"/>
>   </xsl:template>
>
>   <xsl:template match="navigate">
>     <xsl:if test='ancestor::top_bar'>
>       <xsl:param name="navColor">#ffffff</xsl:param>
>     </xsl:if>
>     <xsl:if test='ancestor::bottom_bar'>
>       <xsl:param name="navColor">#000000</xsl:param>
>     </xsl:if>

I don't know whether I understand it correctly what you want to reach here.
But I think you want to know from which template (top or bottom_bar) you are
"called", don't you? The axes refer to the XML, so this test for ancestor
can only work if you have a structure like

<top_bar>
    <navigate>
    </navigate>
</top_bar>

If you really only want to know whether you are at the bottom or at the top,
it is better to pass a parameter fom one template to the other.

<xsl:apply-templates select="foo">
    <xsl:with-param name="param" select="value"/>
</xsl:apply-templates>

For your stylesheet it can look so:

<xsl:template match="top_bar">
    <xsl:apply-templates select="document('nav.xml')">
        <xsl:with-param name="position" select="'top'"/>
    </xsl:apply-templates>
</xsl:template>

Pay attention on the single quotes around top. This means you want to sent
top as string. Otherwise a top-element will be searched and the value of
this will be used.

Now you have a new problem. The above code applies a template to the root
node '/' of nav.xml. You should add the document element there, which I
think is <navigate>, because the param will be passed to the template
matching on '/', but no step further.

<xsl:apply-templates select="document('nav.xml')/navigate">

>     <xsl:if test='ancestor::top_bar'>
>       <xsl:param name="navColor">#ffffff</xsl:param>
>     </xsl:if>

But your code in this template has more errors. You create a param not at
the very beginning of the template. You should use xsl:variable here. And
the param/variable is only available at the childs/descendants of the
<xsl:if>. If you are outside <xsl:if>, you will get an error "$navColor is
out of scope" or so. You have to put the variable outside the xsl:if:

<xsl:variable name="navColor">
    <xsl:if test="bla">...</xsl:if>
    <xsl:if test="foo">...</xsl:if>
</xsl:variable>

Futhermore you can use xsl:choose, xsl:when, xsl:otherwise here as you did
it in the code below. Here it makes sense.

>     <xsl:value-of select="$navColor"/>
>     <xsl:for-each select="lnk">
>       <a href="{@href}" style="color:{$navColor};
> font-size:9pt"><xsl:value-of select="."/></a>
>       <xsl:choose>
>         <xsl:when test="position() == last()">

But here not ;-) A simple xsl:if is enough here (if you haven't shortened
the code). And the doubled '=' should be wrong too.

>         </xsl:when>
>         <xsl:otherwise>
>           <span style="color:{$navColor}"><xsl:text> | </xsl:text></span>

You don't need <xsl:text> here if you write it in one line.

>         </xsl:otherwise>
>       </xsl:choose>
>     </xsl:for-each>
>   </xsl:template>

So at the end I have the following templates:

<xsl:template name="top_bar">
    <xsl:apply-templates select="document('./nav.xml')">
        <xsl:with-param name="position" select="'top'"/>
    </xsl:apply-templates>
</xsl:template>

<xsl:template name="bottom_bar">
    <xsl:apply-templates select="document('./nav.xml')">

        <xsl:with-param name="position" select="'bottom'"/>
    </xsl:apply-templates>
</xsl:template>

<xsl:template match="navigate">
    <xsl:param name="position"/>
    <xsl:variable name="navColor">
        <xsl:choose>
            <xsl:when test="$position = 'top'">#ffffff</xsl:when>
            <xsl:when test="$position = 'bottom'">#000000</xsl:when>
        </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="$navColor"/>
    <xsl:for-each select="lnk">
        <a href="{@href}"
style="color:{$navColor};font-size:9pt"><xsl:value-of select="."/></a>
        <xsl:if test="position() != last()">
            <span style="color:{$navColor}"> | </span>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

If it's not necessary to know whether you are creating top-nav or bottom-nav
at the moment, but only the color, you can pass the navColor directly to the
navigate template (again as string). The template is a bit shorter then.

<xsl:template name="top_bar">
    <xsl:apply-templates select="document('./nav.xml')">
        <xsl:with-param name="navColor" select="'#ffffff'"/>
    </xsl:apply-templates>
</xsl:template>

<xsl:template match="navigate">
    <xsl:param name="navColor"/>
    <xsl:value-of select="$navColor"/>
    ...
</xsl:template>

> Basically, I want the color for the top_bar to be white and the bottom bar
> to be black, but it's not applying.  I also tried using:  <xsl:template
> match="//navigate">  but that didn't work either.

This does not change anything. As the name says it is only a "template". An
the matcher says on which nodes to match. If you write match="navigate"
(match navigate elements without limitations) or match="//navigate" (match
navigate elements anywhere in the tree without limitations) there is no
difference - maybe it changes the priority of the template. The arbitrative
is the xsl:apply-templates. There you select on which nodes a template
should be applied. This is why I add on the xsl:apply-templates on
document('nav.xml') the /navigate.

> (is it not working because it's importing from an outside file?)

No, definitively not. With external documents you have to pay attention on
some special things (as the new root node), but they behave exactly as the
input tree.

Hope this helps (nothing of the code is tested, it's difficult without
knowing the exact XML too),

Joerg


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


Current Thread