12.10.2008

XSL: применение шаблона с «внутренним» условием

Часто бывает нужно применить шаблон для элемента только если этот элемент (или его содержимое) удовлетворяет некоторому условию. Простой пример: применить шаблон для элемента <Brands> если внутри есть хотя бы один элемент <Brand>. Рассмотрим несколько вариантов как можно сделать эту проверку.

Вариант 1

<xsl:template match="Something">
    <xsl:if test="Brands/Brand">
        <xsl:apply-templates select="Brands"/>
    </xsl:if>
</xsl:template>

<xsl:template match="Brands">
    <h2>Производители</h2>
    <ul>
        <xsl:apply-templates select="Brand"/>
    </ul>
</xsl:template>

Неудачное решение, поскольку, написав проверку в шаблоне для <Something>, мы поместили в этот шаблон знание об особенности трансформации <Brands>.

Вариант 2

<xsl:template match="Something">
    <xsl:apply-templates select="Brands[Brand]"/>
</xsl:template>

<xsl:template match="Brands">
    <h2>Производители</h2>
    <ul>
        <xsl:apply-templates select="Brand"/>
    </ul>
</xsl:template>

Немного компактнее, но проблема первого варианта осталась.

Вариант 3

<xsl:template match="Something">
    <xsl:apply-templates select="Brands"/>
</xsl:template>

<xsl:template match="Brands">
    <xsl:if test="Brand">
        <h2>Производители</h2>
        <ul>
            <xsl:apply-templates select="Brand"/>
        </ul>
    </xsl:if>
</xsl:template>

Гораздо лучше. Шаблон для <Something> просто вызывает шаблон для <Brands>, не заморачиваясь о внутреннем устройстве <Brands>. Шаблон для <Brands> сам разбирается как трансформировать <Brands>.

Вариант 4

<xsl:template match="Something">
    <xsl:apply-templates select="Brands"/>
</xsl:template>

<xsl:template match="Brands"/>
<xsl:template match="Brands[Brand]">
    <h2>Производители</h2>
    <ul>
        <xsl:apply-templates select="Brand"/>
    </ul>
</xsl:template>

Я предпочитаю этот вариант. Он также хорошо работает и в случае с несколькими условиями — вместо громоздкого <xsl:choose> мы получаем компактные шаблончики.

<xsl:template match="Brands"/>

<xsl:template match="Brands[Brand]">
    <h2>Производители</h2>
    <ul>
        <xsl:apply-templates select="Brand"/>
    </ul>
</xsl:template>

<xsl:template match="Brands[count(Brand) > 10]">
    <h2>Много производителей</h2>
    <!-- Выводим бренды в три колонки -->
</xsl:template>

4 комментария:

ast комментирует...

Давно только так и делаю :-)
А вот многих совсем недавно обучали использовать чузы и template name всякие

Анонимный комментирует...

Степан, спасибо за классный блог, по КПД уделываете 99% книг по теме ;-)

Недавно начал изучать XSLT, пока что вопросов на порядки больше чем ответов.

Самый наболевший: для каких проектов используется xslt? Обычно сайт делается так:
db->php->html

с xslt наверное так:
db->xml->xslt->html

или даже так:
(каким-то образом полученый)xml->xslt->html

Какой-то здоровенный стек получается :) Как это у вас в Студии организованно?
Спасибо.

Степан Резников комментирует...

У нас обычно так организовано:

db --(parser|java)--> xml --(xslt)--> html

То есть данные хранятся в базе данных, с помощью parser или java они достаются и выдаются в виде xml, далее с помощью xslt они трансформируются в html.

Главное преимущество -- это возможность полностью распараллелить работу серверного программиста и front-end технолога. Серверный программист делает db --(parser|java)--> xml, front-end технолог делает xml --(xslt)--> html, при этом они не мешают друг другу.

В принципе, для этого можно также использовать и другие шаблонизаторы - Smarty, например.

Анонимный комментирует...

Теперь все прояснилось, спасибо :-)