WACT содержит различные средства, которые позволяют собирать конечные шаблоны из набора других шаблонов:
Wact компилирует шаблоны только целиком! То есть сначала обрабатываются все include и wrap теги, и только затем создается откомпилированный шаблон. Именно поэтому разбиение шаблона на составляюще в WACT практически ничего не стоит (лишь увеличивается незначительно время компиляции). Это дает нам возможность выносить в отдельные шаблоны все, что повторяется в нескольких файлах.
Цель данной страницы - показать способы «снижения дублирования» в WACT-шаблонах, то есть как максимально эффективно использовать все возможности WACT по композиции шаблонов.
Включение (include) одного шаблона в другой. Include в WACT осуществляется посредством тега <core:include file='path_to_file'/>. По-умолчанию, включаемый файл компилируется как обычный шаблон.
<core:include file='header.html'/> [...content...] <core:include file='footer.html'/>
Если вы используете WACT совместно с пакетом WEB_APP, и в атрибуте file используется абсолютное значение (начинается с / или с c:/), тогда WACT подключает шаблон по абсолютному пути. В противном случае - пытается найти шаблон через механизм подключения шаблонов.
Компиляцию включаемого шаблона можно отключить, если использовать атрибут literal, например: <core:include file='path_to_file' literal='true'/>.
Обворачивание (wrap) - это вставка шаблона в определенное место другого шаблона.
С обворачиванием шаблонов связаны следующие термины:
Обворачивание производится при помощи тега <core:wrap file='file_to_wrap_into'>. Контейнер обычно помечается тегом <core:placeholder id='some_id'/>.
Рассмотрим небольшой пример:
page.html - базовый шаблон
<html> <head> [...meta...] </head> <body> <core:placeholder id='content'/> </body> </html>
my_page.html - текущий шаблон
<core:wrap file='page.html' as='content'> [...content...] </core:wrap>
В результате получим нечто:
<html> <head> [...meta...] </head> <body> [...content...] </body> </html>
Разберем подробнее, как работает тег <core:wrap>.
<core:wrap> требует закрывающего тега. Обворачиваемой считается та часть текущего шаблона, что обрамлена в рамки тега <core:wrap>.
Если тег <core:wrap> не содержит атрибута file, тогда обворачивание производится в шаблон, который был указан родительским тегом <core:wrap>. Таким образом мы можем как бы указать сначала целевой файл, а затем только указать какие части и куда обвернуть.
Поясним это все на примере:
Допустим есть базовый шаблон страницы page.html:
<html> <body> <core:placeholder id='content'/> </body> </html>
Также шаблон дополнительного дизайна layout.html с двумя местами для вставки (как бы второй базовый шаблон):
<div id='header'><core:placeholder id='header'/></div> <div id='main'><core:placeholder id='main'/></div>
И теперь текущий шаблон display.html:
<core:wrap file='page.html' as='content'> <core:wrap file='layout.html'> <core:wrap as='header'>My Header</core:wrap> <core:wrap as='main'>My Complex content</core:wrap> </core:wrap> </core:wrap>
На выходе получим:
<html> <body> <div id='header'>My Header</div> <div id='main'>My Complex content</div> </body> </html>
Необходимо пояснить, как именно компилируется шаблон в данном случае. Сначала обрабатывается первый <core:wrap> и мы получаем на 1-м шаге компиляции следующий шаблон:
<html> <body> <core:wrap file='layout.html'> <core:wrap as='header'>My Header</core:wrap> <core:wrap as='main'>My Complex content</core:wrap> </core:wrap> </body> </html>
Затем обрабатывается второй <core:wrap> и условно получается:
<html> <body> <!-- Дочерние <core:wrap> теги могут искать контейнеры вставки только в этих рамках --> <div id='header'><core:placeholder id='header'/></div> <div id='main'><core:placeholder id='main'/></div> <core:wrap as='header'>My Header</core:wrap> <core:wrap as='main'>My Complex content</core:wrap> <!-- ...... --> </body> </html>
Обратите внимание, что дочерние <core:wrap> теги (без атрибута file) могут найти контейнеры вставки только в отмеченной зоне (только внутри шаблона, указанного родительским <core:wrap>).
В результате работы дочерних <core:wrap> получается конечный результат.
Контейнер вставки не обязательно должен быть реализован при помощи тега <core:placeholder>. Для этих целей можно использовать вообще любой тег, например <div>. Необходимо будет лишь указать, что этот тег должен обрабатываться как тег с активным компонентом и дать ему идентификатор. Это можно сделать при помощи одного атрибута: wact:id, например:
<div wact:id='content'>Content will be placed here</div>
<core:wrap> можно использовать по-разному:
Для этого теперь есть 2 атрибута:
В первом случае содержимое между <core:wrap> будет вставлено сразу же после содержимого контейнера вставки, во втором - полностью заменит контент внутри контейнера.
Самый сложный момент, который обычно представляет трудность для верстальщиков - это понимание, как будет выглядеть откомпилированный шаблон, а также понимание структуры контейнеров данных, которая будет существовать в шаблоне после компиляции.
Разберем небольшой пример (предупреждаем, он может показаться весьма нетривильным для понимания).
Путь у нас есть базовый шаблон page.html, в котором есть 2 контейнера вставки:
<html> <body> <h1><core:placeholder id='title'/></h1> <div wact:id='content'></div> </body> </html>
Предположим, что у нас есть такой текущий шаблон:
<core:wrap file='page.html'> <core:datasource id='my_data'> <core:wrap in='title'>{$title}</core:wrap> <core:wrap in='content'>{$content}</core:wrap> </core:datasource> </core:wrap>
На первый взгляд все нормально, мы должны получить конечный шаблон, где из контейнера данных my_data будут выводиться значения title и content. Однако это вовсе не так!!
На самом деле шаблон откомпилируется схематично следующим образом:
<html> <body> <h1>{$title}</h1> <div id='content'>{$content}</div> </body> </html> <core:datasource id='my_data'> </core:datasource>
То есть по сути внутри контейнера данных ничего нет! А переменные, которые в исходном шаблоне как бы относились к контексту my_data, на самом деле в откомпилированном шаблоне относятся именно к корневому контейнеру данных.
Здесь есть 3 решения:
<core:datasource>
Первый вариант будет выглядеть сдедующим образом (изменится только текущий шаблон):
<core:datasource id='my_data'> <core:wrap file='page.html'> <core:wrap in='title'>{$title}</core:wrap> <core:wrap in='content'>{$content}</core:wrap> </core:wrap> </core:datasource>
Самый большой недостаток этого подхода - это то, что теперь весь шаблон заключен в активный компонент тега <core:datasource id='my_data'>, то есть мы как бы имеем теперь 2 корневых шаблона. Это может повлять на работу некоторых шаблонов, которые выводили некоторые переменные, считая что текущий контейнер будет глобальным, например, {$meta_keywords} вместо {$#meta_keywords}.
Второй способ более правильный, но требует выделения еще одного шаблона. Вот как будут выглядеть все наши шаблоны:
Базовый шаблон page.html
<html> <body> <core:placeholder id='content'/> </body> </html>
Шаблон layout.html
<h1>{$title}</h1> <div wact:id='content'>{$content}</div>
И наш текущий шаблон:
<core:wrap file='page.html' in='content'> <core:datasource id='my_data'> <core:wrap file='layout.html'> <core:wrap in='title'>{$title}</core:wrap> <core:wrap in='content'>{$content}</core:wrap> </core:wrap> </core:datasource> </core:wrap>
Теперь все будет работать как ожидается.
Обсуждение