Using Named Templates for Frequently Used Templates

MSXML 5.0 SDK

Microsoft XML Core Services (MSXML) 5.0 for Microsoft Office - XSLT Developer's Guide

Using Named Templates for Frequently Used Templates

This topic demonstrates the use of a named template as a boilerplate (that is, a frequently used template that is designed to be reusable).

We will enhance the region_qtr.xsl style sheet in Initial Example for Named Templates to display company-wide results, as well as results for the individual regions. The display of the overall results will be similar to the display of individual results: a level-3 heading, followed by a table. The final result is region_qtr_param.xsl.

The following is the relevant portion of the initial region_qtr.xsl style sheet, which displays data for each sales region, but not for all regions together.

<xsl:template match="sales">
    <h1>Quarterly Sales by Region</h1>
    <h2>Quarter Beginning <xsl:value-of select="@quarter"/></h2>
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="region">
    <h3>
        <xsl:value-of select="name"/> Region
        (Manager: <xsl:value-of select="manager"/>)
    </h3>
    <table width="100%">
        <tr>
            <th width="33%"># Units</th>
            <th width="33%" >Qtr Sales Amount</th>
            <th>Amt/Unit</th>
        </tr>
        <tr>
            <td width="33%" align="right">
                <xsl:value-of select="format-number(units, '#,##0')"/>
            </td>
            <td width="33%" align="right">
                <xsl:value-of select="format-number(sales_amt, '#,##0.00')"/>
            </td>
            <td align="right">
                <xsl:value-of select="format-number(sales_amt div units, '#,##0.000')"/>
            </td>
        </tr>
    </table>
</xsl:template>

The <xsl:template match="sales"> template rule contains an <xsl:apply-templates> statement, which invokes <xsl:template match="region"> to select and process the four <region> elements in region_qtr.xml.

First, add the company-wide table to the template rule for the <sales> element. This information should follow the <apply-templates> element, like this (the changes are in bold):

<xsl:template match="sales">
    <h1>Quarterly Sales by Region</h1>
    <h2>Quarter Beginning <xsl:value-of select="@quarter"/></h2>
    <xsl:apply-templates/>
    <h3>Company-Wide Results</h3>
    <table width="100%">
        <tr>
            <th width="33%"># Units</th>
            <th width="33%">Qtr Sales Amount</th>
            <th>Amt/Unit</th>
        </tr>
        <tr>
            [cells for company-wide data]
        </tr>
    </table>
</xsl:template>

Note that the structure of the first company-wide table row is identical to the corresponding row for the regional tables. This is an ideal situation for a reusable named template—that is, for a portion of a template, or a complete template, that you want to re-use multiple times in your style sheet.

To create such a named template, copy the reusable portion into an <xsl:template> rule with a name attribute, as follows:

<xsl:template name="table_hdgs">
    <tr>
        <th width="33%"># Units</th>
        <th width="33%">Qtr Sales Amount</th>
        <th>Amt/Unit</th>
    </tr>
</xsl:template>
Note   The portion to be extracted to a named template must be well-formed. In this case, even though the identical portions of the two template rules include the <table> start tag and the <tr> start tag that follows the first row, the following would not be correct:
<xsl:template name="common_table"> <table>
<tr>
<th width="33%"># Units</th>
<th width="33%">Qtr Sales Amount</th>
<th>Amt/Unit</th>
</tr>
<tr>
</xsl:template>
This named template would be rejected by the XSLT processor, because it is not well-formed.

The second step is to replace, in the style sheet, each occurrence of the extracted code with an <xsl:call-template> element. The name attribute of this element must match the name attribute of the named template itself.

When the second step is complete, the template rules for the <sales> and <region> elements look like this:

<xsl:template match="sales">
    <h1>Quarterly Sales by Region</h1>
    <h2>Quarter Beginning <xsl:value-of select="@quarter"/></h2>
    <xsl:apply-templates/>
    <h3>Company-Wide Results</h3>
    <table width="100%">
        <xsl:call-template name="common_table"/>
        <tr>
            [cells for company-wide data]
        </tr>
    </table>
</xsl:template>

<xsl:template match="region">
    <h3>
        <xsl:value-of select="name"/> Region
        (Manager: <xsl:value-of select="manager"/>)
    </h3>
    <table width="100%">
        <xsl:call-template name="common_table"/>
        <tr>
            <td width="33%" align="right">
                <xsl:value-of select="format-number(units, '#,##0')"/>
            </td>
            <td width="33%" align="right">
                <xsl:value-of select="format-number(sales_amt, '#,##0.00')"/>
            </td>
            <td align="right">
                <xsl:value-of select="format-number(sales_amt div units, '#,##0.000')"/>
            </td>
        </tr>
    </table>
</xsl:template>

This makes each template rule shorter and easier to understand. It also simplifies maintenance. If you want to make a change, such as adding a new column or changing the formatting, the change only has to be made once. This change is made to the named template, instead of once for every occurrence of the boilerplate template.

The next topic, Passing Parameters Using Named Templates, continues to build this example.