Templates in stylesheets each match
particular nodes in the node tree. The match attribute on a template tells the XSLT processor what kind of nodes
they match. In a full stylesheet, you tell the XSLT processor what nodes you
want to be processed (using an <xsl:apply-templates> instruction, as you'll see later) and the XSLT processor goes
through the nodes you've selected one by one trying to find a matching template
for
each in turn. When it looks for a template, it searches the whole stylesheet,
so it doesn't matter where the template is within that stylesheet.
When the XSLT processor finds a matching
template, it uses the content of that template to process the matching node and
generate some output. The content of the template might include instructions
that tell the processor to apply templates to a particular set of nodes, in
which case it goes through those nodes finding and processing matching
templates, and so on.
In this section, we'll look at the
implications of this processing model and the kinds of changes that we can make
to our stylesheet to take advantage of templates.
XSLT processing involves
telling the processor to apply templates to some nodes in the source document.
The XSLT processor locates a template that matches the node, and processes its
content to generate a result. The location of the template within the
stylesheet doesn't matter.
This process of applying templates to nodes
in the node tree has to start somewhere. In the majority of cases, the input to
the stylesheet is an XML document, and the processor starts at the top of the
node tree, on the root node. After building the node
tree, the XSLT processor takes the root node and tries to find a template that matches it. If the XSLT processor finds one, it processes the content of that template to generate the output.
If
you're running a stylesheet from code, then you can make the stylesheet start
from a node other than the root node if you want. This is useful if you want to
only process a section of a larger document, for example. In these cases, you
need templates that match the nodes that you use as the source of the
transformation – a template that matches the root node will only be used if you
tell the processor to process the root node.
A template that matches the root node has a
match attribute with a value of /, one that looks like:
<xsl:template
match="/">
...
</xsl:template>
If you're
familiar with programming, you can think of this template as analogous to the main() method on a class. Whatever happens, when you process a document with
the stylesheet, the XSLT processor will process the contents of this template,
so it gives you top-level control over what the stylesheet does.
If you look back at the full stylesheet
that you created based on the simplified stylesheet from the last chapter,
you'll see that it contains only one template, which matches the root node. The
stylesheet works because the XSLT processor always activates that template.
A template that matches the
root node acts as high-level control over the result of the stylesheet.
At the moment, we use <xsl:for-each> to tell the XSLT processor to go through the <Channel> elements one by one. That's one place where we could use templates instead. In this section, we'll look at how we
can replace this <xsl:for-each>
with a separate template and an <xsl:apply-templates> instruction.
First, we need a template that tells the XSLT processor what to do when it's told to process a <Channel> element. You can match an element with a template by giving the name of the element in the match attribute of the <xsl:template> element. So the following template will match <Channel> elements and process them to generate the same result as is currently generated inside <xsl:for-each>:
<xsl:template
match="Channel">
<h2
class="channel"><xsl:value-of select="Name"
/></h2>
<xsl:for-each
select="Program">
<div>
<p>
<span
class="date"><xsl:value-of select="Start"
/></span><br />
<span
class="title"><xsl:value-of select="Series"
/></span><br />
<xsl:value-of
select="Description" />
<span
onclick="toggle({Series}Cast);">[Cast]</span>
</p>
<div
id="{Series}Cast" style="display: none;">
<ul
class="castlist">
<xsl:for-each
select="CastList/CastMember">
<li>
<span
class="character">
<xsl:value-of select="Character" />
</span>
<span
class="actor">
<xsl:value-of select="Actor" />
</span>
</li>
</xsl:for-each>
</ul>
</div>
</div>
</xsl:for-each>
</xsl:template>
If a
template's match attribute
gives the name of an element, the XSLT processor will use that template for
elements with that name.
You can put this template wherever you like
at the top level of the stylesheet (at the same level as the other <xsl:template> elements). The XSLT processor will find it and use it whenever it
needs to process a <Channel>
element. In TVGuide3.xsl, I've
put this template after the template that matches the root node, so the top
level of the stylesheet looks like:
<?xml
version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template
match="/">
...
</xsl:template>
<xsl:template
match="Channel">
...
</xsl:template>
</xsl:stylesheet>
I usually order the templates in my stylesheets
starting from the template matching the root node and working down the levels
of the node tree. But you are free to arrange your templates however you like.
However, the XSLT processor will never use
this template unless you tell the processor to process (apply templates to) a <Channel> element. You instruct the XSLT processor to apply templates to some
nodes using the <xsl:apply-templates> instruction. Like <xsl:for-each>, the result of the <xsl:apply-templates> instruction gets inserted into the result of the transformation at
the point where you use the instruction, so while the location of <xsl:template> doesn't matter, the positioning of <xsl:apply-templates> is very important.
The <xsl:apply-templates> instruction has a select
attribute, which tells the XSLT processor which nodes to apply templates to. The select
attribute on <xsl:apply-templates> works in the same way as the select attribute on <xsl:for-each> – you give a path that points to the nodes to which you want to
apply templates.
We want the result to appear at the same
place as the result of the original <xsl:for-each>, and we already know the path to those nodes because we're already
using it on the <xsl:for-each>.
So you can apply templates instead by replacing the <xsl:for-each> with an <xsl:apply-templates> element that has exactly the same select attribute. Doing this in TVGuide4.xsl gives a template matching the root node as follows:
<xsl:template
match="/">
<html>
<head>
<title>TV Guide</title>
...
</head>
<body>
<h1>TV Guide</h1>
<xsl:apply-templates select="/TVGuide/Channel"
/>
</body>
</html>
</xsl:template>
Using templates instead of <xsl:for-each> breaks up the stylesheet into manageable chunks in a similar way to
functions and methods in standard programming languages. One advantage of this
is that you don't have XSLT with lots and lots of indentations, which can
really help with readability! A bigger advantage is that you can use the same template
for similar nodes in different places or on the same node multiple times, as
we'll see later in the chapter. On the down side, the stylesheet no longer
looks as similar to the HTML document that we're producing as it used to, and
to change the result of the stylesheet, you may have to navigate between
multiple templates.
You can replace an <xsl:for-each> element with a template holding its contents and an <xsl:apply-templates> element selecting the nodes you want to process.
We've still used <xsl:for-each> in a couple of other places within TVGuide4.xsl, so let's convert those instances in the same way as we've done with the one that iterated over <Channel> elements.
The next <xsl:for-each> is where we iterate over the <Program> elements, which is not within the template that matches <Channel> elements. We can convert it by first replacing the <xsl:for-each> with an <xsl:apply-templates> element that has the same value for its select attribute:
<xsl:template
match="Channel">
<h2 class="channel"><xsl:value-of
select="Name" /></h2>
<xsl:apply-templates select="Program" />
</xsl:template>
and then creating a template that matches <Program> elements. The new template has the same contents as the old <xsl:for-each> did:
<xsl:template
match="Program">
<div>
<p>
<span
class="date"><xsl:value-of select="Start"
/></span><br />
<span class="title"><xsl:value-of
select="Series" /></span><br />
<xsl:value-of
select="Description" />
<span
onclick="toggle({Series}Cast);">[Cast]</span>
</p>
<div
id="{Series}Cast" style="display: none;">
<ul
class="castlist">
<xsl:for-each
select="CastList/CastMember">
<li>
<span class="character">
<xsl:value-of select="Character" />
</span>
<span class="actor">
<xsl:value-of select="Actor" />
</span>
</li>
</xsl:for-each>
</ul>
</div>
</div>
</xsl:template>
This template also contains an <xsl:for-each>, one that iterates over the <CastMember> element children of <CastList> elements. Again, we can replace this <xsl:for-each> with an <xsl:apply-templates>:
<xsl:template
match="Program">
<div>
<p>
<span class="date"><xsl:value-of
select="Start" /></span><br />
<span class="title"><xsl:value-of
select="Series" /></span><br />
<xsl:value-of select="Description" />
<span
onclick="toggle({Series}Cast);">[Cast]</span>
</p>
<div id="{Series}Cast" style="display:
none;">
<ul class="castlist">
<xsl:apply-templates
select="CastList/CastMember" />
</ul>
</div>
</div>
</xsl:template>
and create a separate template that deals
with giving output for <CastMember> elements:
<xsl:template
match="CastMember">
<li>
<span
class="character"><xsl:value-of select="Character"
/></span>
<span
class="actor"><xsl:value-of select="Actor"
/></span>
</li>
</xsl:template>
We now have four templates in TVGuide5.xsl, matching:
The root node
<Channel>
elements
<Program>
elements
<CastMember>
elements
If you run TVGuide5.xsl with TVGuide.xml, you
should get exactly the same result as the original, simplified stylesheet. Splitting up the processing
into separate templates hasn't changed the result of
the transformation.
Buy Beginning XSLT here
© Copyright 2002 Wrox Press
This chapter is written by Jeni Tennison
and taken from "Beginning XSLT" published by Wrox Press Limited in June 2002; ISBN 1861005946; copyright © Wrox Press Limited 2002; all rights reserved.
No part of these chapters may be reproduced, stored in a retrieval system or transmitted in any form or by any means -- electronic, electrostatic, mechanical, photocopying, recording or otherwise -- without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.
|
|