Макеты XSLT с областью динамического содержимого

Я работаю над перестройкой пользовательского интерфейса нашего веб-сайта, который полностью основан на javascript/ajax (без веской причины и довольно неэффективно), так что теперь бэкэнд будет выполнять большую часть генерации контента. Это приложение на C# .net.

Почти все наши страницы (которых, вероятно, 40-50 страниц) имеют одинаковый базовый макет. Я новичок в XSLT, но я проделал большую работу с фреймворками MVC, такими как Spring (java, используя Sitemesh для разметки), Symfony (PHP), немного рельсов, а также несколько других. Мне нравится иметь возможность иметь один или несколько общих шаблонов, а затем иметь определенный раздел "контент", в который попадает материал, относящийся к конкретной странице. Я не могу понять, как это сделать с XSLT. В случае этого приложения у меня есть значение, доступное мне в xml, поддерживающем страницу xslt, давайте назовем его ContentXSL, значением которого является имя файла xsl, который я хочу использовать для раздела содержимого страницы. Я знаю, что это невозможно, но было бы неплохо использовать:

 <xsl:call-template name="{$ContentXSL}" />

Тогда я мог бы просто поместить это в раздел содержимого. Однако это невозможно, поэтому вместо этого мне понадобится массивный оператор выбора, который вызывает правильный шаблон на основе переменной ContentPage. Это также означает, что в моем файле Layout.xsl я пришлось бы включать все документы 40-50 xsl.. Я думаю, что накладные расходы будут довольно большими, но я не уверен в этом. Разумно ли это делать, если сайт получает много трафика?

Каковы другие распространенные способы сделать это? Похоже, что большинство современных фреймворков позволяют использовать этот шаблон для украшения контента. В случае с Symfony он работал очень хорошо и был довольно гибким (со слотами и всем этим).

Я знаю, что другое потенциальное решение - иметь 40 независимых файлов, которые имеют одинаковую разметку и содержат специальные разделы, такие как верхний и нижний колонтитулы. Это означает, что если я хочу изменить общую структуру макета моего сайта, мне придется редактировать все 40-50 страниц (что очень раздражает).

Обновление - больше объяснений

Я хочу дополнительно объяснить это, потому что у меня есть определенные требования, которые потребуют значительных технических изменений. Прежде всего, бэкэнд передаст мне какой-то xml, который даст мне знать о аргументах запроса в URL сайта. Кроме того, он передаст мне данные, необходимые для построения моей страницы (данные в форма бизнес-данных, без HTML или что-то подобное). Данные выглядят примерно так:

<xml>
 <section>Blogs</section>
 <page>showAll</section>
  <data>
   <blogs>
     <blog>
       <author>somebody</author>
       <title></title>
       <content>..</content>
    </blog>
    </blog>..</blog>
   </blogs>    
  </data>
</xml>

Теперь нужно иметь шаблон страницы, подобный этому:

<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:msxsl='urn:schemas-microsoft-com:xslt'>  
 <xsl:output omit-xml-declaration='yes' method='html' media-type='text/html' indent='yes' />
 <xsl:include href="Header.xsl"/>
 <xsl:include href="Nav.xsl"/> 
 <xsl:template name='MainLayout' match='*'>
 <html>
  <head>
   <title></title>
  </head>
  <body>
    <div id="header"><xsl:call-template name="Header" /></div>
    <div id="nav"><xsl:call-template name="Nav" /></div>
    <div id="content">
      [here is where i want to use the xsl from {/xml/section}/{/xml/page}.xsl]
    </div>
  </body>
</html>
</xsl:template>    
</xsl:stylesheet>

Теперь для содержания этой страницы у меня будет следующий файл: Blogs/showAll.xsl

<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:msxsl='urn:schemas-microsoft-com:xslt'>  
      <xsl:output omit-xml-declaration='yes' method='html' media-type='text/html' indent='yes' />
<xsl:template name='Blogs_ShowAll'>
  <div id="blogs-showAll">
   ..iterate over /xml/data/blogs converting to html
  </div>
</xsl:template>
</xsl:stylesheet>

Решения до сих пор были хорошими, но только одно из них я смог полностью переварить (тот, который упоминает, включая все файлы xsl и используя xsl: выберите, чтобы выбрать правильный). Я не уверен, как применить метод FXSL к рассматриваемой проблеме. Обратите внимание, что я не буду возражать против использования подхода типа sitemesh, в котором я указываю теги html / body и все это в дочернем элементе, чтобы он заменял то, что у меня есть в разделе body дочернего элемента, на div содержимого макета (также, если в дочернем элементе есть тег заголовка, замените тег заголовка в макете - все в таком духе).

4 ответа

Решение

ОП предоставил дополнительную информацию о своей проблеме, и этот ответ предоставляет дополнительное решение, которое сейчас запрашивается.

I. Идея:

Это преобразование:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <html>
   <xsl:apply-templates select="*/page"/>
  </html>
 </xsl:template>

 <xsl:template match="page[. = 'showAll']">
   <!-- Transform all data to html -->
   <xsl:apply-templates select="../*/blogs" mode="showAll"/>
 </xsl:template>

 <xsl:template match="page[. = 'showBrief']">
   <!-- Transform the data to Summary html -->
      <xsl:apply-templates select="../*/blogs" mode="showBrief"/>
 </xsl:template>

 <xsl:template match="blogs" mode="showAll">
  <h1>All Blogs: </h1>
  <table border="1">
    <xsl:apply-templates mode="showAll"/>
  </table>
 </xsl:template>

 <xsl:template match="blog" mode="showAll">
  <tr>
    <td>Blog of <xsl:value-of select="author"/></td>
    <td><xsl:value-of select="title"/></td>
  </tr>
  <tr>
    <td colspan="2"><xsl:apply-templates select="content/node()" mode="showAll"/></td>
  </tr>
  <xsl:if test="not(position()=last())">
   <tr><td colspan="2">&#xA0;</td></tr>
  </xsl:if>
 </xsl:template>

  <xsl:template match="blogs" mode="showBrief">
  <h1>Blogs Summary: </h1>
  <table border="1">
    <xsl:apply-templates mode="showBrief"/>
  </table>
  </xsl:template>

   <xsl:template match="blog" mode="showBrief">
     <tr>
      <td>
        <xsl:value-of select="concat(author, ': ', title)"/>
      </td>
     </tr>
   </xsl:template>

</xsl:stylesheet>

при применении к этому XML-документу (на основе предоставленного XML-текста, но делая его корректным и более содержательным):

<xml>
 <section>Blogs</section>
 <page>showAll</page>
 <data>
   <blogs>
     <blog>
       <author>John Smith</author>
       <title>All about golden fish</title>
       <content>
       Here I publish my latest achievements
       in raising golden fish.
       </content>
    </blog>
     <blog>
       <author>Mary Jones</author>
       <title>Knitting, Knitting, Knitting</title>
       <content>
       How to knit a sharf.
       </content>
    </blog>
   </blogs>
 </data>
</xml>

производит желаемый тип вывода "show-all":

<html>
   <h1>All Blogs: </h1>
   <table border="1">
      <tr>
         <td>Blog of John Smith</td>
         <td>All about golden fish</td>
      </tr>
      <tr>
         <td colspan="2">
            Here I publish my latest achievements
                   in raising golden fish.

         </td>
      </tr>
      <tr>
         <td colspan="2">&nbsp;</td>
      </tr>
      <tr>
         <td>Blog of Mary Jones</td>
         <td>Knitting, Knitting, Knitting</td>
      </tr>
      <tr>
         <td colspan="2">
                   How to knit a sharf.

         </td>
      </tr>
   </table>
</html>

Теперь мы изменим документ XML и заменим page элемент с этим:

 <page>showBrief</page>

Когда то же преобразование применяется к обновленному XML-документу, теперь он производит желаемый итоговый вывод:

<html>
   <h1>Blogs Summary: </h1>
   <table border="1">
      <tr>
         <td>John Smith: All about golden fish</td>
      </tr>
      <tr>
         <td>Mary Jones: Knitting, Knitting, Knitting</td>
      </tr>
   </table>
</html>

II. Следующий шаг

На практике все шаблоны в данном режиме будут находиться в отдельном файле xsl и будут импортированы основной таблицей стилей:

Таким образом, преобразование (первичная таблица стилей) становится:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:import href="showAll.xsl"/>
 <xsl:import href="showBrief.xsl"/>

 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <html>
   <xsl:apply-templates select="*/page"/>
  </html>
 </xsl:template>
</xsl:stylesheet>

Сделать примечание:

  1. Преобразование не знает заранее, какие шаблоны будут применены - преобразование полностью управляется данными.

  2. Шаблоны, которые не существуют сейчас, могут быть написаны в будущем и будут применяться без необходимости изменения основной таблицы стилей.

  3. Там нет условной логики, <xsl:choose> инструкции и т. д. Это истинная сила шаблонов xsl в действии.

  4. Это преобразование основано на той же идее, на которой основан FXSL.

 <xsl:call-template name="{$ContentXSL}" />

Хотя это синтаксически недопустимо во всех версиях XSLT, использование шаблонов XSLT в качестве функций более высокого порядка было реализовано и использовалось в библиотеке FXSL уже десять лет назад.

Вот несколько упрощенная идея о том, как этого можно достичь:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:param name="pFunction1">
   <fun name="increment"/>
 </xsl:param>

 <xsl:param name="pFunction2">
   <fun name="double"/>
 </xsl:param>

 <xsl:variable name="vFunIncrement" select=
 "document('')/*/xsl:param[@name='pFunction1']/*"/>

 <xsl:variable name="vFunDouble" select=
 "document('')/*/xsl:param[@name='pFunction2']/*"/>

 <xsl:variable name="vInput" select="."/>

 <xsl:template match="/">
  increment(<xsl:value-of select="$vInput"/>) = <xsl:text/>

  <xsl:apply-templates select="$vFunIncrement">
    <xsl:with-param name="parg1" select="$vInput"/>
  </xsl:apply-templates>

  double(<xsl:value-of select="$vInput"/>) = <xsl:text/>

  <xsl:apply-templates select="$vFunDouble">
    <xsl:with-param name="parg1" select="$vInput"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="fun[@name='double']">
  <xsl:param name="parg1"/>

  <xsl:value-of select="2*$parg1"/>
 </xsl:template>

 <xsl:template match="fun[@name='increment']">
  <xsl:param name="parg1"/>

  <xsl:value-of select="$parg1+1"/>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к следующему документу XML:

<num>2</num>

результат:

  increment(2) = 3

  double(2) = 4

Сделать примечание:

  1. <fun> элементы могут быть переданы извне в преобразование через параметры глобального уровня. Это означает, что преобразование не знает, какие функции будут выполняться.

  2. Функции моделируются путем сопоставления шаблоновfun элементы, которые имеют определенное значение для их name приписывать.

Если вы хотите прочитать и понять FXSL, это два лучших материала:

  1. FXSL 1.x (для XSLT 1.0)
  2. FXSL 2.0 (для XSLT 2.0)

Пример Димитра хорош..

Вот способ сделать это также.. немного уродливое решение, но делает трюк

primary.xsl

<xsl:variable name="ContentXSL" select="/your/xml/settings/@content" />

<!-- Reference templates -->
<xsl:include href="template1.xsl" />
<xsl:include href="template2.xsl" />
<xsl:include href="template3.xsl" />
<xsl:include href="template4.xsl" />

<xsl:template match="/">
  <html>
    <head>
      <title>..</title>
    </head>
  </html>
  <body>
    <xsl:call-template name="getcontent" />
  </body>
</xsl:template>

<xsl:template name="getcontent">
  <xsl:choose>
    <xsl:when test="$ContentXSL = 'template1'">
      <xsl:apply-templates match="/your/xml/structure" mode="template1" />
    </xsl:when>
    <xsl:when test="$ContentXSL = 'template2'">
      <xsl:apply-templates match="/your/xml/structure" mode="template2" />
    </xsl:when>
    <xsl:when test="$ContentXSL = 'template3'">
      <xsl:apply-templates match="/your/xml/structure" mode="template3" />
    </xsl:when>
    <xsl:when test="$ContentXSL = 'template4'">
      <xsl:apply-templates match="/your/xml/structure" mode="template4" />
    </xsl:when>
    <xsl:otherwise>
      <!-- Default template? -->
      <xsl:apply-templates match="/your/xml/structure" mode="template1" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

template1.xsl

<xsl:template match="/your/xml/structure" mode="template1">
  Template 1<br />
</xsl:template>

template2.xsl

<xsl:template match="/your/xml/structure" mode="template2">
  Template 2<br />
</xsl:template>

template3.xsl

<xsl:template match="/your/xml/structure" mode="template3">
  Template 3<br />
</xsl:template>

template4.xsl

<xsl:template match="/your/xml/structure" mode="template4">
  Template 4<br />
</xsl:template>

Помимо превосходного ответа Димитре, рекомендующего метод для реализации функций более высокого порядка, вы также можете использовать метод с мастер-страницами и дочерними страницами в сочетании с неким кодом, например:

MasterContent.xml:

<title>Test for XSLT</title>

MasterLayout.xml:

<html>
    <head>
        <title></title>
    </head>
    <body>
        <p>This is master page</p>
    </body>
</html>

Master.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="pMasterLayout" select="document('MasterLayout.xml')"/>
    <xsl:param name="pMasterContent" select="document('MasterContent.xml')"/>
    <xsl:output method="xml"/>
    <xsl:template match="/">
        <xsl:apply-templates select="$pMasterLayout/*"/>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="title">
        <xsl:copy>
            <xsl:value-of select="$pMasterContent/title"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

ChildLayout:

<html>
    <head>
        <title></title>
    </head>
    <body>
        <h1></h1>
    </body>
</html>

Итак, это преобразование ("Child.xsl"):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:include href="Master.xsl"/>
    <xsl:param name="pChildLayout" select="document('ChildLayout.xml')"/>
    <xsl:param name="pChildContent" select="/"/>
    <xsl:template match="body">
        <xsl:copy>
            <xsl:apply-templates select="$pChildLayout/html/body/*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="h1">
        <xsl:copy>
            <xsl:value-of select="$pChildContent/header"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

С этим входом ("ChildContent"):

<header>Child Content</header>

Выход:

<html>
    <head>
        <title>Test for XSLT</title>
    </head>
    <body>
        <h1>Child Content</h1>
    </body>
</html>

Примечание:

Проверьте лучший пример жизни на araneda bienesraices.com.ar Я рекомендую использовать @id в качестве якорей для заполнения макета содержимым (вы можете удалить их с пустыми шаблонами). Этот метод не привязывает вас к какой-либо IDE поставщика (с понятием XSLT) для создания страниц макета.

Другие вопросы по тегам