15 окт. 2008 г.

Нестандартное использование xsl:copy-of. Разделение содержания и представления

Обычно конструкцию вида xsl:copy-of используют для копирования фрагмента исходного дерева в результирующее дерево. При этом забывая, что xsl:copy-of можно использовать для копирования фрагмента результирующего дерева. Вроде бы, польза от этого неочевидна, однако, я покажу, как это можно применить.

Допустим, у нас есть верстка какого-то сложного html-блока, причем этот блок может повторяться во многих местах, но с разным контентом. В качестве примера рассмотрим блок с закругленными углами, так как обычно это достаточно сложная html-конструкция, состоящая из нескольких html-элементов:

<div class="rounded"> <div class="corner l-t"></div> <div class="corner r-t"></div> <div class="corner r-b"></div> <div class="corner l-b"></div> <div class="content"> <!-- контент --> </div> </div>

И мы хотим обрамлять такой конструкцией свои блоки, допустим — статьи, формы, меню. Решение в лоб — в шаблоне вывода каждого такого блока добавлять такую обрамляющую конструкцию. Т.е. «копипаст» со всеми вытекающими из него последствиями.

Примерно, это выглядело бы так (возьмем блок статей):

<xsl:template match="articles"> <div class="rounded"> <div class="corner l-t"></div> <div class="corner r-t"></div> <div class="corner r-b"></div> <div class="corner l-b"></div> <div class="content"> <xsl:apply-templates select="article"/> </div> </div> </xsl:template>

Мой вариант — создать для обрамляющей html-конструкции отдельный именованный шаблон, а контент передавать ему параметром, который внутри шаблона копировать с помошью xsl:copy-of:

<xsl:template name="block"> <xsl:param name="content"/> <div class="rounded"> <div class="corner l-t"></div> <div class="corner r-t"></div> <div class="corner r-b"></div> <div class="corner l-b"></div> <div class="content"> <xsl:copy-of select="$content"/> </div> </div> </xsl:template>

Шаблоны соответствующих блоков (статей, форм, меню) будут выглядеть следующим образом:

<xsl:template match="articles"> <xsl:call-template name="block"> <xsl:with-param name="content"> <xsl:apply-templates select="article"/> </xsl:with-param> </xsl:call-template> </xsl:template> <xsl:template match="navigation"> <xsl:call-template name="block"> <xsl:with-param name="content"> <ul class="navigation"> <xsl:apply-templates select="navigation-item"/> </ul> </xsl:with-param> </xsl:call-template> </xsl:template> <xsl:template match="form"> <xsl:call-template name="block"> <xsl:with-param name="content"> <xsl:apply-templates select="." mode="zforms"/> </xsl:with-param> </xsl:call-template> </xsl:template>

Получилось, что шаблоны ничего не знают о том окружении, в котором будет находиться сгенерированный ими контент. Таким образом, вводится дополнительный уровень абстракции, отделяющий содержание от представления. Все вышенаписанное — это всего лишь один пример, как можно применить подобный подход, который можно использовать и для более сложных случаев.

P.S. Впервые похожую идею я когда-то услышал от Владимира Токмакова из Студии.