В прошлой статье я рассказал основы использования плагина jQuery Templates plugin и обещал описать возможности синтаксиса шаблонов, чем сегодня и займусь. В этой статье помимо описания инструкций шаблонов будет много примеров, а в конце есть ссылка на демонстрационную страницу с «живыми» примерами – там их можно посмотреть, поредактировать, поэкспериментировать, попробовать собственные примеры.

Синтаксис шаблонов довольно функционален. В теле шаблона разработчик может обращаться к полям объекта, к полям полей объекта, вызывать методы объекта и его полей, обращаться к внешним объектам JavaScript и jQuery, просто писать JS-выражения. Кроме всего этого внутри шаблона можно итеративно выводить коллекции, организовать условный вывод, использовать вложенные шаблоны. Но давайте обо всём по порядку.

Примеры в статье

Все примеры я буду приводить ключевыми кусками, чтобы не загромождать материал повторяющимися частями кода. В примере будут присутствовать текст шаблона, JSON данных, при необходимости JSON опций (параметр options метода .tmpl( ) ) и результат. В общем случае полный код примера будет выглядеть так:

<script type="text/x-jquery-tmpl">
    <li> ЭТО ТЕКСТ ШАБЛОНА </li>
</script>
<script type="text/javascript">
var data = [
    // ЭТО МАССИВ ДАННЫХ
];
var options = {
    // ЭТО ОПЦИИ
};
$(function() {
    // ПРИМЕНЕНИЕ ШАБЛОНА И ВЫВОД РЕЗУЛЬТАТА:
    $('script[type="text/x-jquery-tmpl"]').tmpl(data, options).appendTo($('.output').empty());
});
</script>
<ul class="output"></ul>

Так будет полностью выглядеть приведенный ниже пример 1.:

<script type="text/x-jquery-tmpl">
    <li>${Name} ({{html Desc}}); вычисления: ${p1 + p2 + 10}</li>
</script>
<script type="text/javascript">
var data = [
    { Name: "obj1", Desc: "<em>Object one</em>", p1: 1, p2: 10 },
    { Name: "<em>obj2</em>", Desc: "<strong>Object two</strong>", p1: 3.3, p2: 5 }
];
var options = {
};
$(function() {
    $('script[type="text/x-jquery-tmpl"]').tmpl(data, options).appendTo($('.output').empty());
});
</script>
<ul class="output"></ul>

Вставка данных в шаблон ${}, {{= }}, {{html }}

С помощью этих инструкции можно вставить в шаблон значение поля объекта данных, результат работы метода или результат вычисления некоторого js-выражения, внутри инструкции можно обращаться к глобальным объектам JS, а также к служебным объектам, созданным при обработке шаблона.

Прежде чем перейти к примерам, хочу немного акцентировать внимание на сходстве и разнице этих инструкций. Все три инструкции предназначены для вывода результата в эту точку шаблона. ${expression} и {{= expression}} эквивалентны (первая инструкция – это сокращённая форма второй), результат вставляется в шаблон в виде текста, если в тексте встречаются символы разметки (<  >  & и пр.), то они будут заменены на соответствующие спецсимволы (&lt;  &gt;  &amp; и пр.). Для вставки же разметки с сохранением html-кода нужно использовать инструкцию {{html expression}}.

Пример 1. Вывод данных с разметкой и без

Шаблон:

<li>${Name} ({{html Desc}}); вычисления: ${p1 + p2 + 10}</li>

Данные:

[
    { Name: "obj1", Desc: "<em>Object one</em>", p1: 1, p2: 10 },
    { Name: "<em>obj2</em>", Desc: "<strong>Object two</strong>", p1: 3.3, p2: 5 }
]

Результат:

<ul class="output">
    <li>obj1 (<em>Object one</em>); вычисления: 21</li>
    <li>&lt;em&gt;obj2&lt;/em&gt; (<strong>Object two</strong>); вычисления: 18.3</li>
</ul>

Доступ к объектам и внутренние параметры шаблона

Как я уже упоминал, внутри выражения можно обращаться к глобальным объектам и функциям JS, при этом шаблонизатор сам пытается определить, является указанное имя ссылкой на глобальный объект или на часть переданных данных. И конечно же, локальные данные имеют более высокий приоритет, чем глобальные. Давайте определим в документе глобальные объект testObj и функцию testFunc и попробуем их использовать:

var testObj = {
    Value: 1
};

function testFunc(i) {
    return i % 2 == 1;
}

Пример 2. Использование глобальных объектов

Шаблон:

<li>${Name}; testObj.Value = ${testObj.Value}; testFunc(${p}) => ${testFunc(p)}</li>

Данные:

[
    { Name: "obj1", p: 3, testObj: { Value: 20 } },
    { Name: "obj2", p: 13, testFunc: function(i) { return i + 7; } }
]

Результат:

<ul class="output">
    <li>obj1; testObj.Value = 20; testFunc(3) =&gt; true</li>
    <li>obj2; testObj.Value = 1; testFunc(13) =&gt; 20</li>
</ul>

Внутри шаблона есть ещё доступ  к специальным объектам – внутренним параметрам шаблона: $item и $data.

$item – представляет доступ к обрабатываемой в данный момент единице данных для вставки в шаблон, внутри этого объекта собраны и параметры, переданные пользователем, и дополнительные параметры из аргумента options функции .tmpl( ). Доступ к параметрам из данных осуществляется через вложенный объект .data, а к параметрам аргумента options – напрямую, т.е. если вызвать $(‘#myTmpl’).tmpl({ p1: 1 }, { p2: 2 }), то в шаблоне получить доступ к ним можно так: $item.data.p1 и $item.p2.

$data – это сокращённая версия доступа к данным, по сути это просто синоним для $item.data, в свете этого доступ в шаблоне к p1 и p2 из вызова в абзаце выше может быть таким: $data.p1 и $item.p2.

Пример 3. Параметр options

Шаблон:

<li>${Name}; p * 100 = ${$item.Method($data.p)}</li>

Данные:

[
    { Name: "obj1", p: 3.3 },
    { Name: "obj2", p: 0.13 }
]

Опции:

{ Method: function(i) { return i * 100; } }

Результат:

<ul class="output">
    <li>obj1; p * 100 = 330</li>
    <li>obj2; p * 100 = 13</li>
</ul>

Условная разметка в шаблоне {{if }} {{else}} {{/if}}

Внутри шаблона можно отслеживать некие условия и в зависимости от них выводить тот или иной кусок. Для достижения этих целей применяются следующие комбинации инструкций:

{{if expression}} tmplPart {{/if}} – простой вывод части шаблона, только если выражение истинно,
{{if expression}} tmplPart1 {{else}} tmplPart2 {{/if}} – вывод части 1 или 2 в зависимости от истинности выражения,
{{if expr1}} tmplPart1 {{else expr2}} tmplPart2 {{else}} tmplPart3 {{/if}} – это уже построение цепочки последовательных проверок (if .. else if .. else if .. else ..), понятное дело, что последний else без выражения необязателен. Думаю, тут всё понятно, давайте посмотрим простенький пример:

Пример 4. Условный вывод

Шаблон:

<li>
{{if (
    (typeof(useFirstLastName) != "undefined" ? useFirstLastName : $item.defaultUseFirstLastName) &&
    $data.FirstName && $data.LastName)}} ${FirstName + " " + LastName}
{{else $data.FullName}} ${FullName}
{{else $data.Name}} ${Name}
{{else}} ${$item.NoName}
{{/if}}
</li>

Данные:

[
    { Name: "obj1", FullName: "Object 1", FirstName: "Object", LastName: "One" },
    { FirstName: "Object", LastName: "Two", Name: "obj2", useFirstLastName: true },
    { Title: "object number 3" }
]

Опции:

{ NoName: "[noname]", defaultUseFirstLastName: false }

Результат:

<ul class="output">
    <li>  Object 1  </li>
    <li>  Object Two  </li>
    <li>  [noname]  </li>
</ul>

Поясню, что происходит в шаблоне. Под первым условием мы проверяем, нужно ли выводить имя объекта в виде конкатенации FirstName и LastName – при этом проверяется условие внутри объекта, если оно не определено, то условие в опциях, и требуется наличие обоих свойств. Обращаясь в условии к этим свойствам через параметр $data, гарантируем их наличие именно в переданных данных, а глобальные объекты при этом игнорируются. Во втором и третьем условиях проверяем последовательно свойства FullName и Name – выводим первое попавшееся, если нет ни одного из них, то выводим «заглушку» из опций.

Итеративный обход с помощью {{each }}

Думаю всем знакома функция jQuery .each(func) (а есть ещё и $.map(array, callback) ) и надеюсь многим знакома конструкция JS for (var key in obj) – эти выражения совершают итеративный обход элементов коллекции. В шаблонах тоже возможен вывод коллекций поэлементно и описывается он следующей инструкцией: {{each( index, value ) data}} tmplText {{/each}}. index и value – это имена параметров (указываются без кавычек) для доступа соответственно к текущему индексу и значению в коллекции. Есть и сокращённая форма этой инструкции: {{each data}} tmplText {{/each}} – при этом в тексте шаблона к текущим индексу и значению можно обратиться по именам по умолчанию: $index и $value. Выражение data должно возвращать в результате массив или объект, объект будет обработан как словарь (коллекция пар имя-значение свойств объекта). Довольно слов, посмотрим на примере:

Пример 5. Вывод массива

Шаблон:

<li>
    <table border="1" cellpadding="5" cellspacing="0">
        <tr><th align="left">n</th><th align="left">${Expr}</th></tr>
        {{each NumCollection}}
        <tr><td>${$index}</td><td>${$value}</td></tr>
        {{/each}}
        <tr><td>${$item.EtcName}</td><td>${$item.EtcVal}</td></tr>
    </table>
</li>

Данные:

[
    { Expr: "2^n", NumCollection: [ 1, 2, 4, 8, 16 ] },
    { Expr: "2n-1", NumCollection: [ 1, 3, 5, 7, 9 ] },
]

Опции:

{ EtcName: "etc.", EtcVal: "..." }

Результат:

<ul class="output">
<li>
     <table border="1" cellpadding="5" cellspacing="0">
         <tbody>
             <tr><th align="left">n</th><th align="left">2^n</th></tr>
             <tr><td>0</td><td>1</td></tr>
             <tr><td>1</td><td>2</td></tr>
             <tr><td>2</td><td>4</td></tr>
             <tr><td>3</td><td>8</td></tr>
             <tr><td>4</td><td>16</td></tr>
             <tr><td>etc.</td><td>...</td></tr>
         </tbody>
    </table>
</li><li>
     <table border="1" cellpadding="5" cellspacing="0">
         <tbody>
             <tr><th align="left">n</th><th align="left">2n-1</th></tr>
             <tr><td>0</td><td>1</td></tr>
             <tr><td>1</td><td>3</td></tr>
             <tr><td>2</td><td>5</td></tr>
             <tr><td>3</td><td>7</td></tr>
             <tr><td>4</td><td>9</td></tr>
             <tr><td>etc.</td><td>...</td></tr>
        </tbody>
    </table>
</li>
</ul>

Пример 6. Вывод объекта-словаря

Шаблон:

<li>
JSON = { {{each(key, val) $data}}{{if $item.f}},{{/if}} "${key}": "${val}"{{if $item.f=true}}{{/if}}{{/each}} }
</li>

Данные:

[
    { Name: "obj1", FullName: "Object 1", FirstName: "Object", LastName: "One" },
    { FirstName: "Object", LastName: "Two", Name: "obj2" },
    { Title: "object number 3" }
]

Опции:

{ f: false }

Результат:

<ul class="output">
    <li> JSON = {  "Name": "obj1", "FullName": "Object 1", "FirstName": "Object", "LastName": "One" } </li>
    <li> JSON = {  "FirstName": "Object", "LastName": "Two", "Name": "obj2" } </li>
    <li> JSON = {  "Title": "object number 3" } </li>
</ul>

Вывод шаблона в шаблоне {{tmpl }}

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

Полный вид инструкции: {{tmpl(data, options) template}}.

(На момент написания статьи в оф.документации показана сигнатура инструкции {{tmpl( [data], [options] ) template}} content {{/tmpl}} – это ОШИБКА, у этой инструкции нет закрывающего тега {{/tmpl}} и, соответственно, никакого содержания content)

Параметры data и options – являются необязательными и имеют то же назначение, что и в функции .tmpl(data, options), если параметры опущены, то в качестве данных используется текущий объект данных $data, а options – пустой объект.
Параметр template может принимать следующие значения: HTML-разметка, HTML- или jQuery-объект, строка, которая является именем скомпилированного шаблона или скомпилированный шаблон, также можно просто передать строку-селектор для jQuery (это недокументированная возможность, но она фигурирует в примерах).

Пример 7. Использование сгенерированного шаблона

Шаблон:

<li> {{tmpl $item.lt+tag+$item.gt+$item.tp+"{content}"+$item.lt+"/"+tag+$item.gt}} </li>

Данные:

[
    { tag: "strong", content: "<em>Hello</em>, " },
    { tag: "em", content: "World!" }
]

Опции:

{ lt: "<", gt: ">", tp: "$" }

Результат:

<ul class="output">
    <li> <strong>&lt;em&gt;Hello&lt;/em&gt;, </strong> </li>
    <li> <em>World!</em> </li>
</ul>

В шаблоне этого примера при генерировании вложенного шаблона используются строковые значение «<», «>», «$» в виде переменных, это сделано для того, чтобы они не воспринимались как разметка и инструкции внешним шаблоном.

Пример 8. Использование шаблона внутри шаблона

Шаблон:

<li>${Name} {{if Items}}<ul>{{tmpl(Items) '#tmplSelector'}}</ul>{{/if}}</li>

Тут строку ‘#tmplSelector’ – следует заменить на корректный селектор (или функцию) получения текущего шаблона. Для моих «живых» примеров можно использовать такой шаблон:

<li>${Name} {{if Items}}<ul>{{tmpl(Items) $('#example8 script[type="text/x-jquery-tmpl"]')}}</ul>{{/if}}</li>

, в нём будет применен начальный шаблон, или

<li>${Name} {{if Items}}<ul>{{tmpl(Items) $('#example8')[0].DemoData.Tmpl}}</ul>{{/if}}</li>

, в нём будет применён отредактированный шаблон – полностью «живой» пример.

Данные:

[
    { Name: "obj1", Items: [ { Name: "obj11", Items: [ { Name: "obj111" }, { Name: "obj112" } ] }, { Name: "obj12" } ] },
    { Name: "obj2" }
]

Опции:

{ }

Результат:

<ul class="output">
    <li>obj1 <ul>
        <li>obj11 <ul>
            <li>obj111 </li>
            <li>obj112 </li>
        </ul></li>
        <li>obj12 </li>
    </ul></li>
    <li>obj2 </li>
</ul>

В этом примере определён рекурсивный шаблон.

Шаблон в шаблоне с HTML-содержанием внутри {{wrap }}

{{wrap (data, options) template}} HTMLcontent {{/wrap}} – эта инструкция очень похожа на инструкцию {{tmpl }}, её параметры data, options и template имеют то же назначение. Отличие инструкции {{wrap }} от {{tmpl }} в том, что она может включать HTML-содержание и затем это содержание можно использовать в применяемом шаблоне.

В шаблоне, который передаётся параметром template, можно достать HTML-разметку, заключённую между {{wrap }} и {{/wrap}} с помощью метода $item.html(filter, onlyText). Первым аргументом можно наложить фильтр в виде селектора jQuery на выбираемые из разметки элементы, а вторым аргументом можно указать, что нужно в результате получить только текст верхнего выбранного элемента.

Пример 9. Использование {{wrap }}

<!DOCTYPE html>
<html>
<head>
  <style>
table { border-collapse:collapse; width:380px; background-color:#f8f8f8; border:2px solid blue; margin:5px; } table td { border:1px solid blue; padding:3px; }
</style>
  <script src="http://code.jquery.com/jquery-latest.min.js"></script>
  <script src="http://nje.github.com/jquery-tmpl/jquery.tmpl.js"></script>
</head>
<body>

<script id="myTmpl" type="text/x-jquery-tmpl">
    The following wraps and reorders some HTML content:
    {{wrap "#tableWrapper"}}
        <h3>One</h3>
        <div>
            First <b>content</b>
        </div>
        <h3>Two</h3>
        <div>
            And <em>more</em> <b>content</b>...
        </div>
    {{/wrap}}

    And this wraps different HTML content:
    {{wrap "#tableWrapper"}}
        <div>
            First <b>div</b>
        </div>
        <div>
            Second <b>div</b>
        </div>
        <div>
            Third <b>div</b>
        </div>
        <h3>first h3</h3>
        <h3>second h3</h3>
        <h3>third h3</h3>
    {{/wrap}}
</script>

<script id="tableWrapper" type="text/x-jquery-tmpl">
    <table><tbody>
        <tr>
            {{each $item.html("h3", true)}}
                <td>
                    ${$value}
                </td>
            {{/each}}
        </tr>
        <tr>
            {{each $item.html("div")}}
                <td>
                    {{html $value}}
                </td>
            {{/each}}
        </tr>
    </tbody></table>
</script>

<div id="myWrappedView"></div>

<script>
$( "#myTmpl" ).tmpl()
    .appendTo( "#myWrappedView" );
</script>

</body>
</html>

Этот пример взят со страницы оф. документации по этой инструкции шаблона. В «живых» примерах приведена переработанная под их специфику версия.

Комментарии в шаблонах {{! }}

В шаблонах можно вставлять комментарии разработчика, которые не будут попадать в результат, делается это очень просто, а именно так:

{{! это текст комментария}}

«Живые» примеры из статьи

По этому адресу страница доступна online: http://zalab.net/projects/jquery/demo/tmpl/
А по этой ссылке можно загрузить эту страничку в архиве: jq.tmpl.live.examples.zip (33 KB)

Поделиться в FaceBookПоделиться ВКонтактеДобавить в TwitterПоделиться в Моём МиреСохранить закладку в GoogleОтправить в Живую ленту GoogleДобавить в Яндекс.ЗакладкиПоделиться в ОдноклассникахОпубликовать в LiveJournal