Search

I'm calling a template called 'excerpt' from several pages, and I'm passing in a param, 'path', which is a node-set that contains a news excerpt I'd like to display.

Currently, I've got it working using <xsl:for-each> like this:

<xsl:template name="excerpt">
    <xsl:param name="path" select="''" />
    <xsl:for-each select="$path">
        <article class="newsarticle">
            <h2><a href="{$root}/news/view/{title/@handle}"><xsl:value-of select="title" /></a></h2>
            <xsl:copy-of select="excerpt/node()" />
        </article>
    </xsl:for-each>
</xsl:template>

…but I'd like to know if it's possible to do it with <xsl:apply-templates>, somehow getting the $path param into the match for <xsl:template>. Like this (which won't work):

<xsl:template name="excerpt">
    <xsl:param name="path" select="''" />
    <xsl:apply-templates select="$path" />
</xsl:template>

<xsl:template match="$path">
    <article class="newsarticle">
        <h2><a href="{$root}/news/view/{title/@handle}"><xsl:value-of select="title" /></a></h2>
        <xsl:copy-of select="excerpt/node()" />
    </article>
</xsl:template>

Yes, you can replace the <xsl:for-each> with a <xsl:apply-templates>.

You could also consider re-factoring this by eliminating the <xsl:call-template> and calling <xsl:apply-templates> like so:

<xsl:template match="path/to/node" mode="excerpt">
    <article class="newsarticle">
        <h2><a href="{$root}/news/view/{title/@handle}"><xsl:value-of select="title" /></a></h2>
        <xsl:copy-of select="excerpt/node()" />
    </article>
</xsl:template>

The trick here is to utilize mode.

Thanks, Lewis. I tried putting everything within a <xsl:apply-templates>, but I just got all the data spat out on to the page; I'll check to see where I'm going wrong later.

The reason I'm calling the template with a parameter is because I've got several datasources serving different pages, each of which serves up a news excerpt, but obviously has a different node-set, because that's based on the name of the datasource, so I don't think your second option applies in my situation. I could be wrong though – I'll go for a walk and think about it.

I tried putting everything within a , but I just got all the data spat out on to the page; I'll check to see where I'm going wrong later.

It sounds like you're select expression was not matching your expected template. As a result, the XSLT processor defaulted to one of its built-in rules which resulted in the blob of text. Make sure your paths, match and modes if you're using them. Always comes down to a typo for me ;)

The reason I'm calling the template with a parameter is because I've got several datasources serving different pages, each of which serves up a news excerpt, but obviously has a different node-set, because that's based on the name of the datasource

A template can match more than one node by doing the following:

<xsl:template match="path/to/node | path/to/other/node" mode="excerpt">
    <article class="newsarticle">
        <h2><a href="{$root}/news/view/{title/@handle}"><xsl:value-of select="title" /></a></h2>
        <xsl:copy-of select="excerpt/node()" />
    </article>
</xsl:template>

Notice the | between the two paths.

Yes, you can replace the <xsl:for-each> with a <xsl:apply-templates>.

When I replace the <xsl:for-each> with a <xsl:apply-templates>, should it be a straight swap, i.e. should I put the template inside the tag pair, like this:

<xsl:apply-templates select="$path>
    <xsl:copy-of select="excerpt/node()" />
</xsl:apply-templates>

I can't get that to work, am I missing something/doing it completely wrong?

I had no idea that templates could match more than one node using | – thanks!

Maybe this will work as you need:

<xsl:template match="*" mode="excerpt">
    <article class="newsarticle">
        <h2><a href="{$root}/news/view/{title/@handle}"><xsl:value-of select="title" /></a></h2>
        <xsl:copy-of select="excerpt/node()" />
    </article>
</xsl:template>

so there is a * for the match. You can apply this template then like

<xsl:apply-templates select="/whatever/path/to/excerpt/entry" mode="excerpt"/>

(the mode attribute is important in this * case)

I am not a guru but I hope it helps.

Thanks, juro – I'd seen something like that mentioned elsewhere using match="node() | @*" and it certainly works.

I'm still wondering if I've missed something in Lewis's <xsl:apply-templates> suggestion, though, or whether I would indeed need a mode and a <xsl:template match="*" mode="excerptmode">, regardless.

Is there anything intrinsically better or worse about using a <xsl:for-each> or a <xsl:apply-templates> in my example? And if I use mode, is one or other better out of match="node() | @*" and match="*"?

If I have the context correct you do not need to utilize a parameter, $path.

If you have the following:

<xsl:apply-templates select="my-data-source/entry">

Then you must have a template that matches the same expression (* matches just about everything):

<xsl:template match="my-data-source/entry">
<article class="newsarticle">
    <h2><a href="{$root}/news/view/{title/@handle}"><xsl:value-of select="title" /></a></h2>
    <xsl:copy-of select="excerpt" />
</article>

Since this is an excerpt, you need to use mode to differentiate it from the template that you use to display the whole entry. Without using mode you would have two templates, matching the same element, and which one used would come down to priority. You can overcome this by using mode. If this is the only template you use that matches that expression (i.e. path), then you do not need to utilize mode.

Does that help?

I'm still using <xsl:apply-templates select="$path" /> (I thought about adding in each of the different possible node-sets using the | as per your earlier suggestion, but I concluded that I'd rather only have to make changes to the file I was updating/creating in future, rather than in that file and the file containing the 'excerpt' named template.

Given that I'm doing it this way, is juro's suggestion above the right way to accomplish it with <xsl:apply-templates> (and/or is there any cost/benefit of using the <xsl:for-each> I mentioned in my first post)?

Given that I'm doing it this way, is juro's suggestion above the right way to accomplish it with

Yes.

<xsl:template match="*" mode="excerpt">

And <xsl:apply-templates /> is almost always better than a <xsl:for-each>.

Excellent; thanks for all your help.

Hi davidpmccormick, You are welcome. Yes, you are best with the picks Lewis also summarized. mode is not a lesser option, its more of a common attribute of daily use. * is simply a full xml continuation for the given node tree.

About the swap back there, it does not work because xsl:apply-templates element can not contain transforming instructions by itself like xsl:copy-of etc. as you I think tried out. It only calls a standalone xsl:template definition, which finaly has this transformations prescribed. That is e.g.:

<!-- Main Template Start -->
<xsl:template match="/">
    ... html ...
    <xsl:apply-templates select="data/news/entry" mode="news"/>       
    ... /html ...
</xsl:template>
<!-- Main Template End -->

<!-- News Detail Template Start -->
<xsl:template match="entry" mode="news">
    <xsl:value-of select="title"/>
</xsl:template>
<!-- News Detail Template End -->    

Thats a bit of difference from using for-each, which is kind of procedural programming, were templates are more of an OOP style and therefore more versatile and used the most (just an idea though - I am not a programmer).

OK. If you'd like to dig more into XSLT coding issues, you can find some insights and references to some good articles in this thread.

Good luck.

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details