Reputation: 67
I started my first adventure with xsl today in hope of doing this "easy" script quickly. Now 4 h later I still dont know why it generate 2 times...
I need your help with finding a error in my script, because I can't find it myself...
I'm creating HTML form based on XML with XSL.
I basically have one template that is automaticly created two times. And I only want the one in <div class="columnDiv">
.
Screenshot with error reasult.
Screenshots with what I want to achieve.
My XML
<?xml version="1.0" encoding="UTF-8"?>
<labels>
<options>
<option type="Top Text 1"/>
<option type="Top Text 2"/>
<option type="Top Text 2"/>
</options>
<statuses>
<status type="Correct">
<answer description="Yes"/>
<answer description="No"/>
</status>
<status type="Error - Type 1">
<answer description="Option 1"/>
<answer description="Option 2"/>
<answer description="Option 3"/>
<answer description="Option 4"/>
</status>
<status type="Error - Type 2">
<answer description="Option 1"/>
<answer description="Option 2"/>
<answer description="Option 3"/>
<answer description="Option 4"/>
</status>
</statuses>
</labels>
My XSL
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<form method="POST" action="new-process.php">
<div class="mainC">
<xsl:apply-templates/>
</div>
<input type="submit" name="submit" value="Submit" />
</form>
</xsl:template>
<xsl:template match="options">
<xsl:for-each select="option">
<div class="columnDiv">
<h3><xsl:value-of select="@type"/></h3>
<xsl:apply-templates select="/labels/statuses"/>
</div>
</xsl:for-each>
</xsl:template>
<xsl:template match="status">
<xsl:for-each select=".">
<label><xsl:value-of select ="@type"/></label>
<select>
<xsl:for-each select="answer">
<option><xsl:value-of select="@description"/></option>
</xsl:for-each>
</select>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Current Output with Comments on Desired Output
<?xml version="1.0" encoding="UTF-8"?>
<form method="POST" action="new-process.php">
<div class="mainC">
<div class="columnDiv">
<h3>Top Text 1</h3>
<label>Correct</label><select><option>Yes</option><option>No</option></select>
<label>Error - Type 1</label><select><option>Option 1</option><option>Option 2</option><option>Option 3</option><option>Option 4</option></select>
<label>Error - Type 2</label><select><option>Option 1</option><option>Option 2</option><option>Option 3</option><option>Option 4</option></select>
</div>
<div class="columnDiv">
<h3>Top Text 2</h3>
<label>Correct</label><select><option>Yes</option><option>No</option></select>
<label>Error - Type 1</label><select><option>Option 1</option><option>Option 2</option><option>Option 3</option><option>Option 4</option></select>
<label>Error - Type 2</label><select><option>Option 1</option><option>Option 2</option><option>Option 3</option><option>Option 4</option></select>
</div>
<div class="columnDiv">
<h3>Top Text 2</h3>
<label>Correct</label><select><option>Yes</option><option>No</option></select>
<label>Error - Type 1</label><select><option>Option 1</option><option>Option 2</option><option>Option 3</option><option>Option 4</option></select>
<label>Error - Type 2</label><select><option>Option 1</option><option>Option 2</option><option>Option 3</option><option>Option 4</option></select>
</div>
<!-- \ this is the output that I do no want \ -->
<label>Correct</label><select><option>Yes</option><option>No</option></select>
<label>Error - Type 1</label><select><option>Option 1</option><option>Option 2</option><option>Option 3</option><option>Option 4</option></select>
<label>Error - Type 2</label><select><option>Option 1</option><option>Option 2</option><option>Option 3</option><option>Option 4</option></select>
<!-- / this is the output that I do no want / -->
</div>
<input type="submit" name="submit" value="Submit"/>
</form>
Upvotes: 0
Views: 162
Reputation: 24470
Per comments, to get the solution you're after you only need to add select="/labels/options"
to your first apply-templates statement. @TimC gives a great explanation of this in his answer.
That said, you've also used a lot of for-each
loops in your code. Generally with XSL it's better to use templates in such scenarios as it makes for cleaner code*. Here's a tweaked version with those loops removed: http://xsltfiddle.liberty-development.net/bFDb2BX/3
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<form method="POST" action="new-process.php">
<div class="mainC">
<xsl:apply-templates select="/labels/options"/>
</div>
<input type="submit" name="submit" value="Submit" />
</form>
</xsl:template>
<xsl:template match="options/option">
<div class="columnDiv">
<h3><xsl:value-of select="@type"/></h3>
<xsl:apply-templates select="/labels/statuses"/>
</div>
</xsl:template>
<xsl:template match="status">
<label><xsl:value-of select ="@type"/></label>
<select>
<xsl:apply-templates select="./answer"/>
</select>
</xsl:template>
<xsl:template match="answer">
<option><xsl:value-of select="@description"/></option>
</xsl:template>
</xsl:stylesheet>
*I'd originally stated that this also improved parallel processing; but as Martin points out in the comments some engines support parallel processing for for-each
too; and I couldn't find any documentation to support my original statement; so suspect it's apocryphal.
Upvotes: 3
Reputation: 70648
You haven't made a bad start at all, but you now need to learn about XSLT's Built in Template Rules. These are templates that will be used when there is no matching template in your XSLT.
You have a template matching the document node /
, in which you do <xsl:apply-templates />
. This will select the label
node, for which you have no template. So the default template is this...
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
And this will select the options
and statuses
nodes. You have a template matching options
so that is good, but not one matching statuses
, so the built-in one kicks in again.
But in your options
template you do <xsl:apply-templates select="/labels/statuses"/>
which ultimately results in the statuses
(and the child status
nodes) getting selected twice.
One solution is to change the <xsl:apply-templates />
in the /
node to explicitly only select options
only, like so...
<xsl:template match="/">
<form method="POST" action="new-process.php">
<div class="mainC">
<xsl:apply-templates select="labels/options"/>
</div>
<input type="submit" name="submit" value="Submit" />
</form>
</xsl:template>
Upvotes: 1