Наследуемые шаблоны в FreeMarker

в 17:37, , рубрики: freemarker, templating, web, Веб-разработка, Программирование, метки: , ,

FreeMarker — достаточно известный шаблонизатор на Java. Сразу оговорюсь, что обсуждать его сильные или слабые стороны не буду. Так сложилось, что мне он пришелся, что называется «по душе» и, можно даже сказать, «пошел в руку». В общем, часто вспоминаю о нем, когда возникает задача генерации текста на основе шаблонов. В основном, конечно, это касается его родной среды — генерации веб страниц. Впрочем, в сочетании, с FMPP и не только веб-страниц.

Тем, кто разрабатывает под ASP.NET хорошо знакома техника создания страниц на основе наследуемых шаблонов, когда в наследниках переопределяются или добавляются определенные блоки страницы, а сами шаблоны могут создавать иерархию. Впрочем, понятно, что идея далеко не новая и интуитивно понятная.

На уровне Java кода, эта задача для FreeMarker прекрасно уже решена. Но вот было стойкое ощущение, что почти то же самое можно сделать и не выходя за его базовые рамки, т.е. использовать в том же FMPP. И, действительно, решение получилось вполне компактным. Хотя и несколько искусственным.

Пусть у нас есть базовый шаблон x-base.ftl и нужно получить файлы x-child-1.html и x-child-2.html. Оба создаются на базе x-base.ftl, но, x-child-2.html в свою очередь, еще и 'наследуется' от x-child-1.html.

Итак, что нам здесь может предложить FreeMarker?

base.ftl

<#global html_block = {}>

<#macro override name>
  <#local  html_tmp><#nested></#local>
  <#local  html_tmp_inherited=html_block[name]!""/>
  <#global html_block = html_block + {name:(html_tmp+html_tmp_inherited)}>
</#macro>

<#macro block name>
  ${html_block[name]!""}
</#macro>

<#macro inherit page>
   <#import page as inh>
</#macro>

<#-- HTML BASE TEMPLATE -->
<#macro template>
<html>
<body>
  <h1>Base template</h1>
  <@block "body"/>
</body>
</html>
</#macro>

x-child-1.html

<#import "x-base.ftl" as b>

<@b.override "body">
	<div>Child 1</div>	
	<@b.block "child2"/>	
</@b.override>

<@b.template />

x-child-2.html

<#import "x-base.ftl" as b>

<@b.override "body">
	<div>Body  - Child 2</div>	
</@b.override>

<@b.override "child2">
	<div>Child 2</div>	
</@b.override>

<@b.inherit "x-child-1.html" />
<@b.template />

'Скормив' вышеприведенные шаблоны FreeMarker, получим на выходе:
x-child-1.html

<html>
<body>
  <h1>Base template</h1>
  	<div>
		Child 1
	</div>	
  </body>
</html>

x-child-2.html

<html>
<body>
  <h1>Base template</h1>
  	<div>
		Child 1
	</div>	
  	<div>
		Child 2
	</div>	
	<div>
		Body  - Child 2
	</div>	
</body>
</html>

Как видим, с достаточно скромными накладными расходами в наше распоряжение поступает принципиальная возможность создания наследуемых шаблонов.
В примере особое внимание следует уделить порядку объявления и использования директив. В частности, директива @b.template в конце файла, собственно, и 'формирует' документ по шаблону и должна быть в его конце. А директива @b.inherit «x-child-1.html» (введена чисто из стилистических соображений, т.к. по сути это просто псевдоним к import), непосредственно перед ней.

В качестве примера применения: Мне данный подход пригодился для генерации статического сайта на основе имеющихся данных с помощью вышеупомянутого FMPP.

Автор: saaivs

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js