Макет цифровых данных в jquery. Официальные плагины jQuery: Templates plugin. Использование переменной $item

С выпуском jQuery, жизнь разработчиков стала значительно проще. Например мы без труда можем сделать следующее:

$("#someElement").children().each(function() { $(this).wrap($("")); });

Этот код поместит всех потомков элемента с id #someElement в тег . В подобных операциях нет ничего плохого; это выражение абсолютно верно составлено и является очень удобным в некоторых ситуациях. Но HTML код, помещённый в наш сценарий - это нарушение структурной логики кода. В этом простом примере это нарушение не является существенным, однако в реальных проектах это очень распространено. Обычно подобный код содержит множество HTML фрагменты, которые выстраиваются в DOM после получения данных из AJAX запросов. Такой стиль может быстро превратиться в нечто, что будет крайне не читабельно.

Использование шаблонов позволит нам устранить этот недостаток, путём отделения HTML фрагментов от скриптов, таким образом разделить логику содержания разных видов кода. В процессе, я не могу не показать вам некоторые супер интересные нововведения, касающиеся AJAX, введённые jQuery 1.5.

Начало

В этом примере мы будем разрабатывать Twitter виджет, который будет загружать не только наши самые последние сообщения, но и список друзей и последователей. Я выбрал Twitter для этого примера, потому что он взаимодействует с данными в формате JSON с которыми работать легко и весело.

Давайте же начнем; сам виджет будет построен на базе следующей HTML структуры:

jQuery, AJAX and Templating Dan Wellman

Husband, father, front-end developer and author. Writes for Nettuts and Packt Publishing. Works for @designhaus | jQuery fanatic

  • Tweets
  • Friends
  • Followers
This widget has super-awesome features which require the use of JavaScript. Please enable it for a better internet experience

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

Используем aside

Вероятнее всего этот виджет, будет выглядеть как боковая панель, и отображать содержание указанного пользователя Twitter. Учитывая это, я решил поместить содержимое в тег . Для того чтобы с лёгкостью получить доступ к этому элементы выставим ему ID.

К слову о разметке. Все заголовки сообщений пользователя Twitter будут включены в тег , а изображение и всё остальное в теге

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

Ещё у нас на странице будут реализованы вкладки для переключения между списками сообщений, друзей и подписчиков. Все они будут заключены в теги

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

    Наконец, у нас есть всё что нужно для того, чтобы разместить все входящие данные: внешний контейнер с ID, три вкладки для сообщений, друзей и последователей, которые также имеют свои ID для удобства выборки. Мы также добавили элемент для тех посетителей, которые не используют JavaScript при просмотре web-страниц. Обратите внимание, что мы будем использовать плагин tmpl, который даст нам возможность использовать шаблоны jQuery. Этот плагин можно скачать .

    Чуть ранее я упомянул о специальных таблицах стилей. Откройте новый файл в вашем любимом текстовом редакторе и добавить следующий код:

    #tweetbox { display:block; width:300px; padding:10px; border:1px solid #aaa; -moz-border-radius:5px; border-radius:5px; font-family:"Trebuchet MS", Arial, Helvetica, sans-serif; background-color:#eee; } #tweetbox img { display:block; } #user { margin-bottom:10px; float:left; } #user h2 { margin:0 0 10px 0; position:relative; font-size:18px; } #user img { float:left; } #user p { width:230px; margin:0; position:relative; float:left; font-size:10px; color:#333; } #user img { display:block; margin-right:10px; border:3px solid #333; } #tools { margin:0; *margin-bottom:-10px; padding:0; clear:both; list-style-type:none; } #tools li { float:left; } #tools a { display:block; height:20px; padding:3px 24px; border:1px solid #aaa; border-bottom:none; -moz-border-radius:5px 5px 0 0; border-radius:5px 5px 0 0; margin-right:-1px; position:relative; font-size:14px; outline:none; background-color:#d6d6d6; background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0.5, #E8E8E8), color-stop(0, #DBDBDB), color-stop(0.5, #D6D6D6)); background-image: -moz-linear-gradient(center top, #E8E8E8 50%, #DBDBDB 0%, #D6D6D6 50%); } a { text-decoration:none; color:#333; } #tools .on { height:21px; margin-top:-1px; top:1px; } #feed { width:298px; border:1px solid #aaa; clear:both; background-color:#d6d6d6; } #feed > div { display:none; } noscript { display:block; padding:10px; font-size:13px; color:#333; }

    Сохраните этот файл как tweetbox.css в том же каталоге, где находятся ваши HTML страницы. Это лишь малое, что мы можем указать для дизайна нашего виджета. В коде, который мы только что привели, вы можете заметить несколько приёмов CSS3: округлённые углы (обратите внимание, мы больше не нуждаемся в -WebKit- для этих целей!), а так же некоторые градиенты для вкладок. Следует отметить, что мы прячем все блоки, за исключением одного - активного. На данный момент виджет должен выглядеть так:

    Добавляем скрипт

    Теперь давайте напишем небольшой скрипт для реализации переключения вкладок. Создайте новый файл и добавьте следующий код:

    (function($) { //табы var tweetbox = $("#tweetbox"), tweetData = null, friendData = null, followData = null; tweetbox.find("#tools a").click(function(e) { e.preventDefault(); var link = $(this), target = link.attr("href").split("#"); tweetbox.find(".on").removeClass("on"); link.addClass("on"); tweetbox.find("#feed > div").hide(); tweetbox.find("#" + target).show(); }); })(jQuery);

    Сохраните этот файл (tweetbox.js) в том же каталоге, где и всё остальное. Тут ничего сложного нет. Вкладки не является сутью урока, так что я не буду долго на этом останавливаться. Скрип работает следующим образом: мы используем анонимную функцию, которая отлавливает тот контейнер который является активным. Мы также инициализируем три переменные, которые будем использовать в дальнейшем. Выставляем их значение в null.

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

    Получаем данные

    Теперь начинается самое интересное. Мы будем делать запрос на Twitter для того, чтобы получить данные для всех 3х вкладок, а так же применим плагин для того, чтобы иметь возможность использовать jQuery шаблоны. Добавьте этот код после предыдущего:

    $.ajaxSetup({ dataType: "jsonp" }); function getTweets() { $.ajax("http://api.twitter.com/statuses/user_timeline/danwellman.json", { success: function(data) { var arr = ; for (var x = 0; x < 5; x++) { var dataItem = {}; dataItem["tweetlink"] = data[x].id_str; dataItem["timestamp"] = convertDate(data, x); dataItem["text"] = breakTweet(data, x); arr.push(dataItem); } tweetData = arr; } }); } function getFriends() { return $.ajax("http://api.twitter.com/1/statuses/friends/danwellman.json", { dataType: "jsonp", success: function(data) { var arr = ; for (var x = 0; x < 5; x++) { var dataItem = {}; dataItem["screenname"] = data[x].screen_name; dataItem["img"] = data[x].profile_image_url; dataItem["name"] = data[x].name; dataItem["desc"] = data[x].description; arr.push(dataItem); } friendData = arr; } }); } function getFollows() { return $.ajax("http://api.twitter.com/1/statuses/followers/danwellman.json", { dataType: "jsonp", success: function(data) { var arr = ; for (var x = 0; x < 5; x++) { var dataItem = {}; dataItem["screenname"] = data[x].screen_name; dataItem["img"] = data[x].profile_image_url; dataItem["name"] = data[x].name; dataItem["desc"] = data[x].description; arr.push(dataItem); } followData = arr; } }); } $.when(getTweets(), getFriends(), getFollows()).then(function(){ // используем шаблоны });

    Сначала мы используем метод ajaxSetup() для того, чтобы указать формат данных, с которыми мы будем иметь дело - jsonp. Поскольку нам предстоит выполнить несколько запросов, лучше один раз указать данное значение.

    Далее мы пишем 3 стандартные по функционалу метода, в которых используем отдельные метод ajax(). Он будет делать запрос на сервис Twitter и возвращать данные. Для запроса мы будем использовать специальные ключевые слова: user_timeline, friends и followers. Чтобы быть уверенными, что данные будут сформированы после удачного запроса, включим наш код в блок success. Каждая из этих функций будет возвращать по 100 хорошо упакованных объектов с Twitter данными.

    Для крока нам не нужно такое количество данных, поэтому мы поместим в массив только по 5 объектов. Для того чтобы быть уверенными, что мы формируем JSON данные в правильном формате, ключи каждого элемента помещаем в двойные кавычки.

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

    Заметьте, что мы не просто вызываем наши три метода - а используем метод when(), который запустит своё содержание только тогда, когда эти три метода закончат свою работу. После того как все три функции вернут success, мы запустим метод then(), где будет располагаться функциональность, касающаяся шаблонов.

    К слову сказать. В данном контексте можно указать и метод fail(), который запустится в том случае, если запросы на Twitter пройдут с ошибкой.

    Всё это очень круто, поскольку в ранних версиях jQuery не был предусмотрен подобный функционал.

    Вспомогательные функции

    В предыдущем примере мы используем некоторые вспомогательные функции convertDate() и breakTweet(). Вот их содержание:

    //формат даты convertDate = function(obj, i) { if (window.ActiveXObject) { obj[i].created_at = obj[i].created_at.replace(/[+]\d{4}/, ""); } var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], date = new Date(obj[i].created_at), formattedTimeStampArray = .created_at], date.toLocaleDateString(), date.toLocaleTimeString()]; return formattedTimeStampArray.join(" "); } breakTweet = function(obj, i) { var text = obj[i].text, brokenTweet = , atExpr = /(@[\w]+)/; if (text.match(atExpr)) { var splitTweet = text.split(atExpr); for (var x = 0, y = splitTweet.length; x < y; x++) { var tmpObj = {}; if (splitTweet[x].indexOf("@") != -1) { tmpObj["Name"] = splitTweet[x]; } else { tmpObj["Text"] = splitTweet[x]; } brokenTweet.push(tmpObj); } } else { var tmpObj = {}; tmpObj["Text"] = text; brokenTweet.push(tmpObj); } return brokenTweet; }

    Функция convertDate(): в первую очередь проверяем, использует ли браузер window.ActiveXObject. Если да, то мы используем JavaScript метод replace(), чтобы удалить Timezone, которая содержится в JSON объекте, возвращённом от Twitter. Этот метод использует регулярные выражения для того чтобы найти и заменить строку.

    Далее мы создаём некоторые переменные; массив, который содержит сокращённые названия месяцев. Дни в датах JavaScript начинаются с 0. Затем мы создаём объект Date, используя конструктор new Date(, и передаём дату, которая хранится в свойстве created_at объекта, переданного в функцию.

    Создадим ещё один массив из 3х элементов: первый это день недели, второй - локализованная дата, третий - локализованное время. После формирования массива, мы его возвращаем.

    Функция breakTweet() немого сложнее. Тут нам необходимо преобразовать обычный текст в JSON массив, в котором каждый элемент это объект, содержащий свойства Name или Text. Они будут использоваться в шаблоне. Сначала мы получаем текст из объекта, который вернул нам Twitter (передали его в функцию). Так же тут применяем регулярное выражение, которое отлавливает наличие @usernames.

    Далее мы сканируем текст, чтобы найти имя пользователя; если оно присутствует, вытаскиваем его. У нас получается массив из текстовых значений. Проходимся по этому массиву, ищем тест содержащий символ @; Если данный символ присутствует, то записываем его с ключом Name. Остальной текст записываем с ключом Text. Сформированные объекты помещаем в массив.

    Вот и всё. Мы только что сформировали объект, который можем передать в jQuery шаблон.

    Работа с шаблоном

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

    //применяем шаблоны tweetbox.find("#tweetTemplate").tmpl(tweetData).appendTo("#tweetList"); tweetbox.find("#ffTemplate").tmpl(friendData).appendTo("#friendList"); tweetbox.find("#ffTemplate").tmpl(followData).appendTo("#followList"); //показываем сообщения tweetbox.find("#tweets").show();

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

    Добавляем jQuery шаблоны

    Снова вернёмся к HTML. Первое что мы сделаем, добавим тег сразу же после пустого ul с id “tweetList”:

    {{each text}} {{if Name}} {{tmpl(Name) "#atTemplate"}} {{else}} ${Text} {{/if}} {{/each}} ${timestamp}

    ${$item.data}

    jQuery шаблоны добавляются на страницу HTML непосредственно через элемент . У каждого такого элемента должен быть выставлен элемент id для того, чтобы они могли быть распознаны и обработаны методом tmpl(). Так же атрибут type должен хранить следующее значение - text/x-jquery-tmpl.

    В первом шаблоне, мы добавляем структуру, которую в последствии хотим выводить в DOM. В частности каждое Twitter сообщение будет заключено в теги ,

    И . Чтобы вставить данные из JSON объекта, переданного через метод tmpl(), мы используем серию шаблонных тегов. Затем используем {{each}} для того, чтобы пройтись по каждому элементу, которое хранится в массиве.

    Это массив содержит все Twitter сообщения. Для каждого из них мы делаем проверку на наличие ключа Name. Если ключ присутствует, используем {{tmpl}} тег. Имя функции, через которую будут проходить данные, мы указываем в скобках после tmpl, а также указываем id шаблона, который будем использовать (более детально рассмотрим его через несколько минут). Если объект не содержит ключ Name, то значит мы имеем дело с обычным текстом, который вставим с помощью ${text}. Это условное достигается с помощью тега {{if}} и {{else}}. После этого мы должны закрыть условие, используя {{/if}, и аналогично завершить цикл {{/foreach}}.

    Теперь нам необходимо создать ссылку, которая будет вести непосредственно на Twitter. Для этого используем ${tweetlink} как часть атрибута href, и свойство ${timestamp}. Это данные, которые мы создали и успешно обработали по запросу user_timeline.

    Таким же образом нам необходимо создать вкладки для отображения наших друзей и подписчиков. Их мы создадим по точно такому же принципу. База для всех одна:

  • ${screenname} ${desc}

    Вот мы и научились передавать данные в шаблон с помощью json и выводить их при помощи конструкции ${data};

    Последний штрих

    Теперь, когда почти всё готово, мы можем добавить еще несколько CSS стилей для того, чтобы всё это выглядело более менее прилично. Добавьте следующий код в конец файла tweetbox.css:

    #feed ul { padding:0; margin:0; } #feed li { padding:0; border-bottom:1px solid #aaa; list-style-type:none; font-size:11px; } #feed li:last-child, #feed li:last-child p { border-bottom:none; } #feed p { padding:10px; margin:0; border-bottom:1px solid #eee; background-image:-webkit-gradient(linear, left bottom, left top, color-stop(0.48, #c2c2c2), color-stop(0.71, #d6d6d6)); background-image:-moz-linear-gradient(center bottom, #c2c2c2 48%, #d6d6d6 71%); } #feed p:after { content:""; display:block; width:100%; height:0; clear:both; } .tweet-link { display:block; margin-top:5px; color:#777; } .img-link { display:block; margin:4px 10px 0 0; float:left; } #feed .username a { font-size:14px; font-weight:bold; } #feed .bio { display:block; margin-top:10px; }

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

    Есть еще одна вещь, которую нам, вероятно, следует реализовать: на данный момент, наше форматирования не работает в IE, поскольку IE не дружит с методом split(). Чтобы устранить эту проблему, мы можем использовать превосходный JavaScript патч, созданный Steven Levithan. Он может быть загружен с: http://blog.stevenlevithan.com/archives/cross-browser-split. Подключим этот код с помощью условного комментария:

    Этот код надо добавить непосредственно перед подключением файла tweetbox.js.

    Заключение

    В этом уроке мы рассмотрели некоторые более продвинутые возможности шаблонов в jQuery. Мы познакомились с итерационным циклом {{each}} и условиями {{if}} и {{else}}.

    Ключевым моментом является то, что новая система шаблонов в jQuery позволяет нам разделить наш код, перемещая все HTML фрагменты из файлов JS и обратно в HTML файл.

    Некоторые могут возразить, что мы сейчас просто увеличиваем количество тегов на нашей странице.

    Однако я думаю, что эта альтернатива более предпочтительна. Элементы в формате $ ("") просто строки (конечно же до того момента как браузер запустится, и они создадутся и вставятся в DOM).

    Мы также посмотрели на новый метод when(), добавленный в JQuery 1.5, который позволяет управлять множеством асинхронных запросов. Далее мы использовали метод then(). Данная функция запустит своё содержание только после завершения всех асинхронных процессов.

    Надеюсь, статья была для вас полезна. Спасибо за внимание.

    Лучший способ начать изучение шаблонов данных – это сразу же приступить к работе с ними. В показаны базовые возможности шаблонов. В этот листинг я включил полный HTML документ из-за способа, которым мы устанавливаем шаблоны, используя элемент script , но я покажу вам релевантные элементы в дальнейших примерах.

    Листинг 12-3: Первый пример шаблона данных Example $(document).ready(function () { var data = [ { name: "Astor", product: "astor", stocklevel: "10", price: 2.99 }, { name: "Daffodil", product: "daffodil", stocklevel: "12", price: 1.99 }, { name: "Rose", product: "rose", stocklevel: "2", price: 4.99 }, { name: "Peony", product: "peony", stocklevel: "0", price: 1.50 }, { name: "Primula", product: "primula", stocklevel: "1", price: 3.12 }, { name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: 0.99 }, ]; }); ${name}: Jacqui"s Flower Shop Place Order

    В следующих разделах я разделю пример и объясню каждую часть. Если данные являются частью документа, они известны как встроенные данные . Альтернативой являются удаленные данные , это когда вы получаете данные от сервера отдельно от документа. Удаленных данных мы коснемся далее в этой главе, но это затрагивает поддержку jQuery для Ajax, что является темой глав 14 и 15.

    Определение данных

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

    Листинг 12-4: Определение данных по цветам var data = [ { name: "Astor", product: "astor", stocklevel: "10", price: 2.99 }, { name: "Daffodil", product: "daffodil", stocklevel: "12", price: 1.99 }, { name: "Rose", product: "rose", stocklevel: "2", price: 4.99 }, { name: "Peony", product: "peony", stocklevel: "0", price: 1.50 }, { name: "Primula", product: "primula", stocklevel: "1", price: 3.12 }, { name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: 0.99 }, ];

    Вы выражаете ваши данные как один или более JavaScript объектов. Библиотека шаблонов jQuery очень гибка, когда речь идет о виде объектов, которые могут быть использованы в качестве данных, но формат, показанный в шаблоне, используется наиболее часто, потому что он соответствует формату данных JSON, о чем я расскажу в главе 14.

    Совет

    JSON важен, потому что он часто используется с Ajax, о чем я расскажу в главах 14 и 15.

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

    Определение шаблона

    Как вы можете себе представить, в сердце библиотеки шаблонов данных находится шаблон данных. Это набор HTML элементов, содержащих метки-заполнители (placeholders), которые соответствуют аспектам объектов данных. В показан шаблон для этого примера.

    Листинг 12-5: Определение шаблона данных ${product} .png" /> ${name} :

    Первая вещь, касаемо шаблона, на которую стоит обратить внимание, – это то, что он содержится внутри элемента script со значением атрибута type равном text/x-jquery-tmpl . Мы делаем это для того, чтобы браузер не интерпретировал содержание шаблона как обычный HTML. Это не совсем обязательно, но это хорошая практика, которая поможет избежать многих потенциальных проблем.

    Вторым моментом, на который стоит обратить внимание, является то, что когда вы определяете шаблон в элементе script , вы назначите имя шаблона, используя атрибут id . В нашем случае шаблон называется flowerTmpl . Вам нужно знать имя шаблона, когда вы его применяете к вашим данным.

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

    Когда библиотека шаблонов обрабатывает шаблон, она замещает любые метки-заполнители данных значениями свойств объекта, с которым работает. Так, например, первый объект в массиве библиотека шаблонов свяжет с меткой ${product} и заменит ее значением свойства единицы продукции, то есть в данном случае astor . Вот эта часть шаблона:

    ${product} .png"/>

    трансформированная в это:

    astor .png"/>

    Вставка значений данных – это только одна из вещей, которые вы можете делать с шаблоном. Далее в этой главе я объясню другие возможности.

    Применение шаблона

    Мы сводим шаблон, используя метод tmpl . Это позволяет указать данные, которые мы хотим использовать, и шаблон, который должен быть к ним применен. В показано использование этого метода.

    Листинг 12-6: $("#flowerTmpl").tmpl(data).appendTo("#row1");

    Я использую jQuery $ функцию, чтобы выбрать элемент, который содержит шаблон, а затем для результата вызываю метод tmpl , передавая в качестве аргумента данные, которые вы хотите обработать.

    Метод tmpl возвращает стандартный jQuery объект, который содержит элементы, полученные из шаблона. В нашем случае я завершаю это набором элементов div , каждый из которых содержит элемент img , label и input , которые были обработаны для одного из объектов в моем массиве данных. Я использую метод appendTo , что вставить полный набор дочерних элементов для row1 . Результат можно увидеть на .

    Рисунок 12-2: Использование шаблона данных Настройка результата

    Мы не получили полностью того результата, который ожидали, потому что вся продукция расположена на одной линии. Но поскольку мы работаем с объектом jQuery , мы можем делать с элементами все, что мы и делали раньше. В показано, как это можно сделать, оперируя результатом метода tmpl .

    Листинг 12-7: Обработка результатов, полученных из шаблона $("#flowerTmpl").tmpl(data) .slice(0, 3).appendTo("#row1").end().end().slice(3).appendTo("#row2"); }); ${name}:

    В этом примере я использую методы slice и end , чтобы сузить и расширить выборку, и метод appendTo , чтобы добавить подмножества элементов, сгенерированных из шаблона, в разные ряды.

    Обратите внимание, что мне пришлось вызвать метод end два раза подряд, чтобы проработать сужение выборки, вызванное методами slice и appendTo . Это очень функционально, и я вообще люблю использовать метод end , чтобы создавать команды в одно выражение, но мне не очень нравится последовательность end().end() . Вместо этого я обычно разделяю такие последовательности на отдельные команды, как показано в .

    Листинг 12-8: Разделение элементов с использованием нескольких выражений var templResult = $("#flowerTmpl").tmpl(data); templResult.slice(0, 3).appendTo("#row1"); templResult.slice(3).appendTo("#row2");

    В любом случае вы получаете тот же результат, то есть продукция поделена по двум рядам, в каждом содержится три цветка, как показано на.

    Рисунок 12-3: Настройка результатов для того, чтобы они подходили верстке
    Корректировка ввода данных

    Можно использовать и другой способ, чтобы скорректировать данные, переданные методу tmpl . В показано, как это можно сделать.

    Листинг 12-9: Использование данных для корректировки результатов из шаблона $(document).ready(function () { var data = [ { name: "Astor", product: "astor", stocklevel: "10", price: 2.99 }, { name: "Daffodil", product: "daffodil", stocklevel: "12", price: 1.99 }, { name: "Rose", product: "rose", stocklevel: "2", price: 4.99 }, { name: "Peony", product: "peony", stocklevel: "0", price: 1.50 }, { name: "Primula", product: "primula", stocklevel: "1", price: 3.12 }, { name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: 0.99 }, ]; var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); }); ${name}:

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

    Вычисление выражений

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

    Листинг 12-10: ${name}:

    В этом шаблоне я использую тройной оператор JavaScript, чтобы установить атрибут value элемента input , основываясь на свойстве stocklevel . Я размещаю это выражение между фигурными скобками, так же как я делал, когда напрямую вставлял значения свойств. Если свойство stocklevel больше нуля, тогда атрибут value будет установлено на 1 , иначе оно будет равно 0 . Результат можно увидеть на . Для всех цветов, кроме пиона, значение stocklevel больше нуля.

    Рисунок 12-4: Вычисление выражения в шаблоне

    Этот пример вкратце демонстрирует основной функционал шаблона: вы комбинируете данные с шаблоном, чтобы получить объекты DOM, которые вы потом добавляете в документ, используя основной jQuery. Затем напрямую или косвенно можно использовать в выражениях значения данных, чтобы сгенерировать содержание.

    Плагин для реализации сменяющихся слов с различными анимированными эффектами. CSS эффект подойдет для реализации промо-сайтов и анимированных баннеров.

    Реализация разбиения содержимого страницы на вкладки (табы) с использованием анимированных CSS3 эффектов. Четыре варианта исполнения: горизонтальные и вертикальные табы с различными анимированными эффектами.

    3. Адаптивный jQuery слайдер на основе плагина IMPRESS.JS

    Классный слайдер с имитацией 3D эффекта при пролистывании слайдов. При увеличении/уменьшении размера экрана - изменяется размер шрифта, изображений и всех элементов на слайде, что гарантирует корректное отображение при любом расширении экрана. Предусмотрена автоматическая прокрутка.

    jQuery решение для создания удобной навигации по разделам документа без перезагрузки страницы. Каждый раздел отображается в отдельном блоке с вертикальной прокруткой, а переход между ними можно осуществить как с помощью клика мыши, так и воспользовавшись заглавием (при большом расширении экранов содержание заглавие отображается с левой стороны, при просмотре сайта на малом экране - сверху документа).

    Реализация навигации по содержимому в виде Список категорий → Список разделов → Содержимое раздела. Навигация сопровождается различными JS эффектами.

    Пролистывание слайдов осуществляется с Parallax эффектом (движение слоев с различной скоростью, в совокупности создают 3D эффект). Размер слайдера изменяется пропорционально изменению размера окна браузера.

    Слайд-шоу с музыкальным сопровождением. Есть возможность ставить на паузу или проматывать музыкальную композицию.

    11. Своеобразное меню на jQuery и CSS

    При выборе пункта меню область с его содержимым раскрывается поверх.

    Выпадающее горизонтальное меню с интересным jQuery эффектом появления вложенных пунктов.

    Обалденный jQuery CSS плагин, будет очень полезным дополнением для сайта любой веб-студии или фрилансера. С его помощью можно наглядно отобразить примеры выполненных работ с разбивкой по времени. Внизу отображается сама временная шкала, а сверху выполненные работы. Для перехода между работами можно использовать как шкалу, так и стрелки влево/вправо.

    14. Галерея «TouchTouch», оптимизированная для просмотра с мобильных устройств 15. CSS3 слайдер изображений «Cycle Slider»

    При клике на название выезжает изображение с описанием. Используется только CSS3.
    Урок по созданию галереи .

    17. Классные яркие всплывающие подсказки jQuery и CSS3

    Всплывающие jQuery подсказки в 7 стилевых оформлениях. Легко подключить и использовать в своих проектах.

    Если изображений в галерее больше 5, то появляются стрелки влево/вправо для навигации по фотографиям.

    Слайдами могут быть не только изображения, но и другой HTML контент.

    Плагин для создания фотореалистичных теней.

    Свежая реализация слайдера.

    26. Перенос текста «Bacon»

    Необычный эффект переноса текста на странице на новую строчку по кривой или заданной линии с определенным шагом смещения.

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

    28. Выбор вида отображения блоков с использованием CSS3

    Четыре вида отображения блоков: изображение с описанием в три колонки, изображение с описанием в одну колонку, только изображения, только текстовое описание. Для переключения вида отображения блоков необходимо воспользоваться пиктограммами сверху.

    Приступаем к работе Давайте начнем. Приведенный ниже пример показывает список фильмов, заданный в массиве (полный код примера - в файле BasicSample1.htm ):

    Простой пример (1) ${title}

    Режиссер: ${director}
    В ролях: ${actors}
    Год: ${year}

    $(function () { $("#movieTmpl").tmpl(dataItems).appendTo("#movieListBag"); }); Простой пример (1)
    А вот что вы увидите в браузере:

    Давайте разберем этот пример подробно.

    Итак, первое, что я делаю, это подключаю jQuery Core Library и jQuery Templates :


    Ранее неоднократно говорилось о том, что jQuery Templates будут включены в jQuery Core Library - но в jQuery 1.5 RC1, вышедшем 24 января, шаблоны по прежнему отсутствуют.

    Затем я загружаю список фильмов:


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

    Внутри файл DataItems.js выглядит следующим образом:

    Var dataItems = [ { title: "Бандиты", thumbnail: "Bandits.jpg", director: "Барри Левинсон", actors: ["Брюс Уиллис", "Билли Боб Торнтон", "Кейт Бланшетт"], year: 2001, budget: 95000000, grossRevenue: 67631903, rating: 0, frames: ["Bandits-1.jpg", "Bandits-2.jpg", "Bandits-3.jpg", "Bandits-4.jpg", "Bandits-5.jpg"] }, ...
    Следующим шагом я создаю шаблон:

    ${title}

    Режиссер: ${director}
    В ролях: ${actors}
    Год: ${year}


    Обратите внимание, что шаблон размещается в теге SCRIPT , а в качестве MIME-типа я указываю text/x-jquery-tmpl . Встретив при разборе документа незнакомый MIME-тип, браузер не пытается интерпретировать содержимое тега SCRIPT , что мне и требуется.

    Вообще говоря, шаблон можно разместить в любом теге, например, в теге DIV :

    ${title}

    Режиссер: ${director}
    В ролях: ${actors}
    Год: ${year}


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

    Например, для приведенного выше примера будет сделана попытка загрузить несуществующую картинку:

    А вот случае с таблицей все может быть гораздо интереснее (большое спасибо TEHEK за этот пример!):

    {{each dataItems}} {{/each}}
    ${title} ${director} ${year}

    Internet Explorer и Opera обработают этот код корректно:

    А вот Chrome и Fire Fox «вытолкнут» лишний код за пределы таблицы, в результате чего таблица окажется пустой… Happy debugging! ;-)

    Для тега SELECT будет наблюдаться аналогичная картина.

    И, наконец, я инстанцирую шаблон с помощью следующего вызова:

    $("#movieTmpl").tmpl(dataItems).appendTo("#movieListBag");
    Что при этом происходит, я изобразил на приведенной ниже диаграмме:

  • Метод .tmpl() получает текст шаблона - т.е. inner text элемента, полученного с помощью вызова $("#movieTmpl") .
  • Текст шаблона компилируется - на его основе создается функция JavaScript.
  • Создается «экземпляр шаблона» - объект, который содержит ссылку на элемент данных (поле data ), переданный как аргумент метода .tmpl() . Методу .tmpl() можно передать массив, объект, null или вызвать его без аргументов. Если передать массив, то для каждого элемента массива будет создан свой экземпляр шаблона, ссылающийся на этот элемент, во всех остальных случаях будет создан только один экземпляр.
  • Вызывается скомпилированная функция-шаблон, которой передается объект-экземпляр. Функция возвращает текст шаблона, в котором сделаны все подстановки.
  • Полученный на предыдущем шаге текст преобразуется в коллекцию HTML-элементов. Ссылки на эти элементы также сохраняются в объекте-экземпляре (поле nodes ), что позволяет в дальнейшем легко обновить «выход» шаблона при изменении исходных данных (см. раздел «Динамическое обновление»).
  • И, наконец, метод .tmpl() возвращает jQuery -коллекцию HTML-элементов, которые добавляются в документ с помощью вызова appendTo("#movieListBag") .
  • Выражения Для подстановки в шаблон значений используется тег ${...} . Внутри этого тега можно указать как наименование свойства объекта, переданного методу .tmpl() , так и любое корректное выражение JavaScript, в том числе вызов функции.

    Использование свойств объекта (элемента массива):

    ${title}
    Использование выражений JavaScript:

    Бюджет: $${(budget / 1000000).toFixed(0)} млн.
    Сборы: $${(grossRevenue / 1000000).toFixed(1)} млн.

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

    Каждый экземпляр шаблона содержит следующие поля:

  • data - содержит ссылку на элемент данных, связанный с экземпляром шаблона;
  • tmpl - содержит ссылку на скомпилированный шаблон, используемый для рендеринга;
  • parent - если шаблон был вызван из другого шаблона с помощью тега {{tmpl}} , содержит ссылку на «родительcкий» экземпляр шаблона;
  • nodes - после рендеринга содержит ссылки на HTML-элементы, порожденные в результате применения шаблона.
  • Кроме того, метод .tmpl() принимает два аргумента - data и options . С аргументом data вы уже познакомились, через него передается ссылка на элемент данных. А используя аргумент options , можно передать ссылку на объект, все поля и методы которого будут перенесены в каждый экземпляр шаблона, созданный в методе .tmpl() .

    Ниже приведен пример использования этого параметра:

    ${title}

    Режиссер: ${director}
    В ролях: ${actors}
    Год: ${year}
    Бюджет: $${$item.formatBudget(budget)} млн.
    Сборы: $${$item.formatGrossRevenue(grossRevenue)} млн.


    $(function () { $("#movieTmpl") .tmpl(dataItems, { formatBudget: function (value) { return (value / 1000000).toFixed(0); }, formatGrossRevenue: function (value) { return (value / 1000000).toFixed(1); } }) .appendTo("#movieListBag"); });
    В этом примере я использую для форматирования значений бюджета и сборов вызовы функций, но чтобы не «захламлять» глобальное пространство имен, я передал их через параметр options , после чего эти функции стали доступны как методы текущего экземпляра шаблона.

    И, наконец, экземпляр шаблона содержит методы update() и html() , использование которых я покажу далее.

    Как выглядит скомпилированный шаблон? Как выглядит скомпилированный шаблон, можно увидеть, воспользовавшись методом .template() , который как раз и осуществляет компиляцию шаблонов. Этот метод возвращает объект-функцию, содержимое которой легко посмотреть:

    $("#compiledTemplateBag").text("" + $("#movieTmpl").template());
    Шаблон, использованный в примере выше, после компиляции выглядит следующим образом (текст отформатирован для лучшей читаемости):

    Function anonymous(jQuery, $item) { var $ = jQuery, call, _ = , $data = $item.data; with ($data) { _.push(" "); if (typeof (title) !== "undefined" && (title) != null) { _.push($.encode((typeof (title) === "function" ? (title).call($item) : (title)))); } _.push("

    Режиссер: "); if (typeof (director) !== "undefined" && (director) != null) { _.push($.encode((typeof (director) === "function" ? (director).call($item) : (director)))); } _.push("
    В ролях: "); if (typeof (actors) !== "undefined" && (actors) != null) { _.push($.encode((typeof (actors) === "function" ? (actors).call($item) : (actors)))); } _.push("
    Год: "); if (typeof (year) !== "undefined" && (year) != null) { _.push($.encode((typeof (year) === "function" ? (year).call($item) : (year)))); } _.push("

    "); } return _; }
    Думаю, что теперь вам должно быть понятно, как обрабатываются выражения, указанные в теге ${...} - и это понимание может существенно помочь вам при отладке! Дело в том, что jQuery Templates выполняет относительно простое преобразование текста шаблона, поэтому если вы допустите ошибку в выражении, то сообщение об ошибке будет относиться к тексту полученной в результате преобразования функции и часто может быть крайне невразумительным.

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

    Что ж, пожалуй, на этом рассказ о работе jQuery Templates стоит завершить и перейти к его практическому применению.

    Часть вторая, практическаяУсловия Для того чтобы применять части шаблона в зависимости от некоторых условий, в jQuery Templates используются теги {{if}}...{{else}}...{{/if}} .

    Приведенный ниже пример показывает использование этих тегов (полный код примера - в файле IfElseTag.htm ):

    Носитель: {{if $item.data.media == "dvd"}} {{else $item.data.media == "blue-ray"}} {{else}} {{/if}}


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

    В качестве условия в тегах {{if}} и {{else}} можно использовать любое корректное JavaScript-выражение.

    Обработка коллекций Для обработки коллекций в шаблонах используется тег {{each}}...{{/each}} . Приведенный ниже пример показывает использование тега {{each}} для вывода списка актеров (полный код примера - в файле EachTag1.htm ):

    В ролях: {{each actors}} ${$value} {{if $index < $data.actors.length - 1}} , {{/if}} {{/each}}

    В качестве аргумента тегу {{each}} можно передать массив, объект, или jQuery -коллекцию. Внутри тег {{each}} использует вызов jQuery.each() , поэтому все, что сказано в документации о поведении jQuery.each() справедливо и для тега {{each}} . Пример ниже демонстрирует использование тега {{each}} для показа всех свойств объекта (полный код примера - в файле EachTag2.htm ):

    {{each $data}} ${$index} ${$value} {{/each}}
    Внутри тега {{each}} доступно две переменные: $value , которая содержит ссылку на текущий элемент массива, и $index , которая содержит индекс текущего элемента массива или имя свойства.

    Разумеется, внутри тега {{each}} можно использовать другие теги, а кроме того, вам по-прежнему будут доступны переменные $item и $data . В приведенном примере переменные $index и $data вместе с тегом {{if}} используются, чтобы вывести запятую между именами актеров - к сожалению, переменная $last не предусмотрена, хотя она была бы очень полезна!

    Наконец, если у вас вдруг возникнет такая необходимость, вы можете изменить имена переменных по умолчанию. В примере, приведенном ниже, эти имена изменены на myIndex и myValue (полный код примера - в файле EachTag3.htm ):

    В ролях: {{each(myIndex, myValue) actors}} ${myValue} {{if myIndex < $data.actors.length - 1}} , {{/if}} {{/each}}
    Кстати, попытка изменить имя только для переменной $index ни к чему хорошему не приведет - ошибки не будет, но и доступа к текущему значению вы тоже не сможете получить!

    Вложенные шаблоны Шаблоны могут быть очень большими - и тогда имеет смысл разделить их на несколько частей меньшего объема или включать повторяющиеся части, которые логично выделить в отдельный шаблон. В jQuery Templates это осуществляется с помощью вложенных шаблонов, для вызова которых используется тег {{tmpl}} .

    Пример ниже иллюстрирует, как вынести часть кода шаблона в другой шаблон (полный код примера - в файле NestedTemplates1.htm ):

    ...

    Режиссер: ${director}
    В ролях: {{tmpl "#actorsTmpl"}}
    Год: ${year}

    ... {{each actors}} ${$value} {{if $index < $data.actors.length - 1}} , {{/if}} {{/each}}
    В теге {{tmpl}} обязательно указывается jQuery -селектор вызываемого шаблона или имя шаблона, ранее сохраненного в кэше. Т.к. в этом примере других аргументов у тега {{tmpl}} нет, вложенный шаблон получит тот же самый элемент данных, что и родительский - но экземпляр шаблона у него будет свой, причем поле parent в нем будет ссылаться на родительский экземпляр шаблона.

    Следующий пример демонстрирует передачу во вложенный шаблон нового элемента данных и использование ссылки на родительский экземпляр шаблона. Как и в случае использования метода .tmpl() , если при вызове вложенного шаблона указать массив, то шаблон будет применен для каждого элемента массива (полный код примера - в файле NestedTemplates2.htm ):

    ...

    Режиссер: ${director}
    В ролях: {{tmpl(actors) "#actors_template"}}
    Год: ${year}

    ... ${$data} {{if $data !== $item.parent.data.actors[$item.parent.data.actors.length - 1]}} , {{/if}}
    И, наконец, последний пример в этом разделе показывает, как передать во вложенный шаблон аргумент options , а заодно демонстрирует, как аргумент options можно использовать для определения последнего элемента в обрабатываемом массиве (полный код примера - в файле NestedTemplates3.htm ):

    ...

    Режиссер: ${director}
    В ролях: {{tmpl(actors, { last: actors }) "#actors_template"}}
    Год: ${year}

    ... ${$data} {{if $data !== $item.last}} , {{/if}}

    Трансформация Еще одна интересная возможность jQuery Templates связана с трансформацией HTML-разметки, для чего используется тег {{wrap}} (вообще говоря, wrap - это «обертывание», но мне кажется что термин «трансформация» лучше отражает суть происходящего).

    Классический пример использования тега {{wrap}} - это создание закладок:

    Вот как это выглядит внутри (полный код примера - в файле Transformation1.htm ):

    {{wrap(null, { viewState: $item.viewState }) "#tabsTmpl"}} English The Ballad of East and West Rudyard Kipling

    OH, East is East, and West is West...

    {{/wrap}}
    Исходные данные для трансформации размещаются в шаблоне tabContent - именно этот шаблон я дальше будут инстанцировать.

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

    Кэширование шаблонов В каждом вызове $("#...").tmpl(...) происходит компиляция шаблона, что, несмотря на резко возросшую скорость работы JavaScript в современных браузерах, может крайне негативно сказаться на производительности. Разработчики jQuery Templates никак не могли обойти своим вниманием этот очевидный факт, поэтому в jQuery Templates предусмотрен механизм для предварительной компиляции и кэширования шаблонов.

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

    $("#movieTmpl").template("movieTmpl");
    Скомпилированный шаблон сохраняется во внутреннем кэше jQuery Templates под именем movieTmpl . Для обращения к кэшированному шаблону используется метод jQuery.tmpl() , и первым параметром ему передается имя кэшированного шаблона:

    $.tmpl("movieTmpl", dataItems).appendTo("#movieListBag");
    В примере, приведенном ниже, осуществляется навигация по списку фильмов, причем для показа информации о фильме используется кэшированный шаблон.

    Код шаблона практически не отличается от тех, что я использовал ранее, единственное его отличие - под описанием фильма дополнительно размещаются ссылки для навигации (полный код примера - в файле CachedTemplates.htm ):

    ${title}

    Режиссер: ${director}
    В ролях: ${actors}
    Год: ${year}

    {{if $item.canMoveBack}} [Назад] {{/if}} {{if $item.canMoveFwd}} [Вперед] {{/if}}
    Сопутствующий скрипт так же несложен:

    Var movieIndex = 0; $(function () { $("#movieTmpl").template("movieTmpl"); updateMovie(); $("#movieBag").delegate(".nav-link", "click", function () { movieIndex += parseInt($(this).attr("x-inc")); updateMovie(); }); }); function updateMovie() { $("#movieBag").empty(); $("#movieBag").append($.tmpl("movieTmpl", dataItems, { canMoveBack: movieIndex > 0, canMoveFwd: movieIndex < dataItems.length - 1 })); }
    Обработчик нажатия на навигационную ссылку меняет индекс выбранного фильма, а затем вызывает функцию updateMovie() , которая сначала очищает контейнер с описанием фильма, а затем заполняет его новыми данными.

    Вот как этот пример выглядит в браузере:


    Браузер, конечно, загрузит соответствующий файл - но вот получить доступ к его содержимому у вас все равно не получится.

    Но шаблон все-таки можно разместить в отдельном файле, и для этого потребуется буквально одна дополнительная строчка кода (полный код примера - в файле ):

    $(function () { $.get("Templates/DynamicLoading.htm", {}, function (templateBody) { $.tmpl(templateBody, dataItems).appendTo("#movieListBag"); }); });
    Т.к. в данном случае мы получаем шаблон в виде текста, для его инстанцирования применяется метод jQuery.tmpl() , первым аргументом которому передается полученный текст шаблона.

    Да, метод jQuery.tmpl() используется для инстанцирования как кэшированных шаблонов по имени, так и шаблонов, заданных в виде текста (традиция!..) - впрочем, он достаточно «умен», чтобы отличить их друг от друга.

    Если вам нужно загрузить несколько связанных шаблонов, вы можете воспользоваться библиотекой WaitSync (см. «Синхронизация асинхронных вызовов. WaitSync ») или написать свой синхронизатор (полный код примера - в файле ):

    $(function () { var ws = new WaitSync(function () { $.tmpl("movieTmpl", dataItems).appendTo("#movieListBag"); }); $.ajax({ cache: false, url: "Templates/MovieTmpl.htm", success: ws.wrap("MovieTmpl", function (templateBody) { $.template("movieTmpl", templateBody); }), error: ws.wrap("MovieTmpl", function () { alert("Error loading MovieTmpl.htm!"); }) }); $.ajax({ cache: false, url: "Templates/ActorsTmpl.htm", success: ws.wrap("ActorsTmpl", function (templateBody) { $.template("actorsTmpl", templateBody); }), error: ws.wrap("ActorsTmpl", function () { alert("Error loading ActorsTmpl.htm!"); }) }); });
    Обратите внимание, что в данном случае шаблон actorsTmpl вызывается по имени (файл Templates\MovieTmpl.htm ):

    Режиссер: ${director}
    В ролях: {{tmpl(actors, { last: actors }) "actorsTmpl"}}
    Год: ${year}

    Динамическое обновление В последнем разделе практической части я покажу еще два сценария работы jQuery Templates - изменение связанных данных и подмену связанного шаблона.

    В приведенном ниже примере для каждого фильма добавлена возможность изменить его рейтинг (полный код примера - в файле DynamicUpdate1.htm ):

    ...
    Сопутствующий код:

    $(function () { $("#movieTmpl").tmpl(dataItems).appendTo("#movieListBag"); $("#movieListBag").delegate(".rating-button", "click", function () { var item = $.tmplItem(this); item.data.rating += parseInt($(this).attr("x-inc")); item.update(); }); });
    Как видно, этот код очень похож на код закладок из раздела «Трансформация», только при работе с закладками я обращался к разделяемому объекту viewState , а здесь я работаю с данными, связанными с экземпляром шаблона.

    В браузере этот пример выглядит так:

    Следующий пример демонстрирует подмену связанного шаблона (полный код примера - в файле DynamicUpdate2.htm ):

    {{tmpl "#movieMainTmpl"}}

    [Больше...]

    {{tmpl "#movieMainTmpl"}}

    Кадры из фильма:

    {{each frames}} {{/each}}

    [Меньше...]


    Здесь я использую два шаблона, movieShortTmpl и movieFullTmpl , общая часть которых вынесена в шаблон movieMainTmpl .

    Сопутствующий код:

    $(function () { var shortTemplate = $("#movieShortTmpl").template("shortTemplate"); var fullTemplate = $("#movieFullTmpl").template(); $.tmpl("shortTemplate", dataItems).appendTo("#movieListBag"); $("#movieListBag").delegate(".more-details", "click", function () { var item = $.tmplItem(this); item.tmpl = item.tmpl === shortTemplate ? fullTemplate: shortTemplate; item.update(); }); });
    Думаю, этот код требует дополнительных пояснений.

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

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

    Заключение Код примеров, использованных в статье, вы можете загрузить по этой ссылке .

    Загрузить jQuery Templates можно с web-сайта ASP.NET CDN или напрямую из репозитария GitHub:
    Brian Landau «Benchmarking Javascript Templating Libraries ».

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

    Теги: Добавить метки

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

    Чтобы не возникало недопонимания, хочу предупредить, что данный модуль не относится к числу активно разрабатываемых или поддерживаемых в настоящее время, и команда разработчиков jQuery не рекомендует его применять. Это не означает, что вы не должны его использовать, однако я счел своим долгом сказать вам об этом, прежде чем вы будете включать его в свои проекты. Я был бы рад порекомендовать вам какой-нибудь другой активно разрабатываемый вариант, однако найти хотя бы близкую по своим возможностям замену jQuery Templates мне пока что не удалось. Но даже при упомянутом отношении к нему разработчиков этот модуль все еще остается наилучшим.

    История модуля jQuery Templates довольно необычна. В свое время Microsoft и команда разработчиков jQuery объявили, что трем подключаемым модулям, разработанным компанией Microsoft, присвоен статус "официальных", чего до того не удостаивался ни один из подключаемых модулей.

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

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

    Настройка библиотеки jQuery Templates

    Прежде чем использовать шаблоны jQuery, нужно загрузить библиотеку jQuery Templates и подключить ее к своему документу.

    Распакуйте архив и скопируйте файл jQuery.tmpl.js (версия для разработки) или jQuery.tmpl.min.js (версия для развертывания) на свой веб-сервер. Следующее, что необходимо сделать - добавить в образец документа элемент script, подключающий библиотеку шаблонов, как показано в примере ниже:

    Библиотека jQuery $(function() { // Сюда будет помещаться код примеров }); h1 { min-width: 70px; border: thick double black; margin-left: auto; margin-right: auto; text-align: center; font-size: x-large; padding: ..png"); background-size: contain; margin-top: 0; } .dtable {display: table;} .drow {display: table-row;} .dcell {display: table-cell; padding: 10px;} .dcell > * {vertical-align: middle} input {width: 2em; text-align: right; border: thin solid black; padding: 2px;} label {width: 6em; padding-left: .5em; display: inline-block;} #buttonDiv {text-align: center;} button {padding: 12px;} #oblock {display: block; margin-left: auto; margin-right: auto; min-width: 700px; } Цветочный магазин Заказать

    Мы будем использовать этот код в качестве образца документа в этой статье. Наверное, вы заметили, что от первоначального варианта, рассмотренного ранее, он отличается не только тем, что в него добавлена библиотека шаблонов, но и тем, что из него удалены элементы, соответствующие различным видам цветочной продукции. Это сделано специально для того, чтобы мы могли восстанавливать эти элементы в документе различными способами с помощью библиотеки шаблонов.

    Внешний вид исходного документа в окне браузера на данном этапе представлен на рисунке:

    Простой пример шаблона данных

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

    ... $(function() { var data = [ { name: "Астра", product: "astor", stocklevel: "10", price: 2.99}, { name: "Нарцисс", product: "daffodil", stocklevel: "12", price: 1.99}, { name: "Роза", product: "rose", stocklevel: "2", price: 4.99}, { name: "Пион", product: "peony", stocklevel: "0", price: 1.50}, { name: "Примула", product: "primula", stocklevel: "1", price: 3.12}, { name: "Подснежник", product: "snowdrop", stocklevel: "15", price: 0.99}, ]; $("#flowerTmpl").tmpl(data).appendTo("#row1"); }); .png"/> ${name}: ...

    В последующих разделах мы разобьем пример на отдельные части и проанализируем код каждой из них по отдельности. Когда данные являются частью документа, они называются встроенными данными (inline data) . Альтернативой им являются дистанционные данные (remote data) , хранящиеся на сервере отдельно от документа. Мы рассмотрим дистанционные данные несколько позже, а пока что можно заметить, что этот вопрос тесно связан с поддержкой Ajax, которую предоставляет библиотека jQuery.

    Определение данных

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

    Var data = [ { name: "Астра", product: "astor", stocklevel: "10", price: 2.99}, { name: "Нарцисс", product: "daffodil", stocklevel: "12", price: 1.99}, { name: "Роза", product: "rose", stocklevel: "2", price: 4.99}, { name: "Пион", product: "peony", stocklevel: "0", price: 1.50}, { name: "Примула", product: "primula", stocklevel: "1", price: 3.12}, { name: "Подснежник", product: "snowdrop", stocklevel: "15", price: 0.99}, ];

    Данные выражаются в виде одного или нескольких объектов JavaScript. Библиотека шаблонов jQuery предоставляет значительную гибкость в выборе объектов, которые могут быть использованы в качестве данных, но представленный выше формат, соответствующий формату данных JSON , является наиболее распространенным. Формат JS0N играет очень важную роль, поскольку его часто используют при работе с Ajax.

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

    Определение шаблона

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

    .png"/> ${name}:

    Первое, на что следует обратить внимание, - это то, что шаблон помещается в элемент script, атрибуту type которого присваивается значение несуществующего типа - text/x-jquery-tmpl . Это сделано для того, чтобы браузер не пытался интерпретировать содержимое шаблона как обычную HTML-разметку. Хотя это и несущественно, но такой практики следует придерживаться, поскольку она чрезвычайно полезна и позволит вам избежать множества потенциальных проблем в будущем.

    Второй момент, на котором я хочу заострить ваше внимание, - это то, что для присвоения имени шаблону, определенному в элементе script, используется атрибут id. В данном случае именем шаблона служит flowerTmpl. Чтобы применить к данным шаблон, необходимо знать его имя.

    Содержимое шаблона будет применено ко всем объектам в массиве данных, что приведет к созданию набора HTML-элементов для каждого объекта. Вы видите, что структура шаблона в целом соответствует набору элементов, которые использовались в предыдущих статьях для представления различных видов цветочной продукции. Главное, чем они отличаются - это элементы кода выполняющие функции заполнителей (data placeholders) .

    В процессе обработки шаблона вместо каждого заполнителя подставляется значение свойства, взятое из текущего объекта. Например, для первого объекта массива вместо заполнителя ${product} будет подставлено значение свойства product, т.е. "astor". Таким образом, часть шаблона

    ${name}:

    преобразуется в следующий HTML-фрагмент:

    Астра:

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

    Применение шаблона

    Для объединения шаблона с данными используется метод tmpl() . При этом вы указываете данные, которые должны использоваться, и применяемый к ним шаблон. Пример использования этого метода приведен ниже:

    $("#flowerTmpl").tmpl(data).appendTo("#row1");

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

    Метод tmpl() возвращает стандартный объект jQuery, который содержит элементы, полученные из шаблона. В данном случае это приводит к набору элементов div, каждый из которых содержит элементы img, label и input, сконфигурированные для одного из объектов, содержащихся в массиве данных. Для вставки всего набора в качестве дочернего элемента в элемент row1 используется метод appendTo(). Результат представлен на рисунке:

    Модификация результата

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

    ... $("#flowerTmpl").tmpl(data) .slice(0, 3).appendTo("#row1").end().end() .slice(3).appendTo("#row2");

    В этом примере методы slice() и end() используются для сужения и расширения набора выбранных элементов, а метод appendTo() - для добавления поднаборов элементов, сгенерированных с помощью шаблона, в различные ряды.

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

    Var templResult = $("#flowerTmpl").tmpl(data); templResult.slice(0, 3).appendTo("#row1"); templResult.slice(3).appendTo("#row2");

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

    Изменение способа предоставления входных данных

    Другой возможный подход заключается в изменении способа передачи данных методу tmpl(). Соответствующий пример приведен ниже:

    Var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2");

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

    Вычисление выражений

    Объекты данных можно использовать не только для получения значений свойств. Если поместить между двумя фигурными скобками выражение JavaScript, то движок шаблонов вычислит его и вставит в сгенерированную шаблоном HTML-разметку. Соответствующий пример приведен ниже:

    ${name}:

    В этом шаблоне значение атрибута value элемента input устанавливается на основании значения свойства stocklevel с помощью тернарного условного оператора. Выражение, заключенное в фигурные скобки, играет ту же роль, какую играло бы записанное вместо него непосредственное значение свойства. Если значение свойства stocklevel больше нуля, то значение value устанавливается равным 1, в противном случае - 0.

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

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

    Использование переменных шаблона

    Шаблоны не являются сценариями JavaScript. Любое содержимое, которое вы добавляете в элемент script, считается частью шаблона и будет включаться в выходной результат. Чтобы сделать шаблоны более гибкими, вам предоставляется небольшое количество контекстных переменных, которые можно использовать в дескрипторах заполнителей. Краткое описание этих переменных содержится в таблице ниже:

    Использование переменной $data

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

    Я всегда стараюсь уменьшить объем кода шаблона до необходимого минимума и поэтому предпочитаю использовать переменную $data внутри функций JavaScript, которые затем вызываю из шаблона. Соответствующий демонстрационный пример приведен ниже:

    $(function() { var data = [ { name: "Астра", product: "astor", stocklevel: "10", price: 2.99}, { name: "Нарцисс", product: "daffodil", stocklevel: "12", price: 1.99}, { name: "Роза", product: "rose", stocklevel: "2", price: 4.99}, { name: "Пион", product: "peony", stocklevel: "0", price: 1.50}, { name: "Примула", product: "primula", stocklevel: "1", price: 3.12}, { name: "Подснежник", product: "snowdrop", stocklevel: "15", price: 0.99}, ]; var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3)).appendTo("#row1"); template.tmpl(data.slice(3)).appendTo("#row2"); }); function stockDisplay(product) { return product.stocklevel > 0 ? 1: 0; } .png"/> ${name}:

    В этом примере определяется функция stockDisplay(), возвращающая значение, которое должно отображаться в элементе input. Аргументом этой функции является объект данных, который мы получаем внутри шаблона с использованием переменной $data. Учитывая, что речь идет всего лишь о простом тернарном операторе, разница в удобочитаемости кода по сравнению с предыдущим вариантом не очень значительна, но в случае более сложных выражений или в случае многократного использования выражения в пределах одного шаблона она будет гораздо более ощутимой.

    Определяя функции, которые будут вызываться из шаблона, будьте внимательны. Дело в том, что такие функции должны определяться до вызова метода tmpl(). Я всегда стараюсь помещать их в конце элемента script, но если функция должна находиться внутри обработчика события ready, то непременно следует убеждаться в том, что она была ранее определена. Другой распространенной ошибкой является то, что функцию часто определяют внутри шаблона.

    Использование функции $() внутри шаблона

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

    Использование переменной $item

    Объект, возвращаемый переменной $item, решает несколько задач. Первая из них - обеспечение возможности обмена дополнительными данными между сценарием JavaScript и шаблоном. Соответствующий пример приведен ниже:

    $(function() { var data = [ { name: "Астра", product: "astor", stocklevel: "10", price: 2.99}, { name: "Нарцисс", product: "daffodil", stocklevel: "12", price: 1.99}, { name: "Роза", product: "rose", stocklevel: "2", price: 4.99}, { name: "Пион", product: "peony", stocklevel: "0", price: 1.50}, { name: "Примула", product: "primula", stocklevel: "1", price: 3.12}, { name: "Подснежник", product: "snowdrop", stocklevel: "15", price: 0.99}, ]; $("Специальное предложение на сегодняшний день: " + "скидка 50 центов") .insertAfter("h1") .css({ color: "red", fontSize: "14pt", textAlign: "center" }); var options = { discount: $("#offer").data("discount"), stockDisplay: function(product) { return product.stocklevel > 0 ? 1: 0; } }; var template = $("#flowerTmpl"); template.tmpl(data.slice(0, 3), options).appendTo("#row1"); template.tmpl(data.slice(3), options).appendTo("#row2"); }); .png"/> ${name}:

    В этом примере мы создаем объект options, для которого определяются свойство (discount) и метод (stockDisplay()). Затем этот объект передается методу tmpl() в качестве второго аргумента. Доступ к свойствам и методам объекта из шаблона обеспечивает переменная $item. Как видите, для обработки скидки в цене, здесь используется свойство discount объекта options.

    Хочу обратить ваше внимание на необходимость включения префикса $ в имена контекстных переменных: $item и $data. Такой же префикс обязателен и в конструкции дескриптора шаблона ${...}, используемой для подстановки значений в шаблон. Пропуск любого из этих префиксов является одной из наиболее распространенных ошибок.

    О других применениях этого объекта мы поговорим далее.

    Использование вложенных шаблонов

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

    ... .png"/> ${name}: {{tmpl($data, $item) "#inputTmpl"}}

    В этом примере шаблон разбит на две части. Первая из них, шаблон flowerTmpl, вызывается для каждого элемента массива данных. В свою очередь, этот шаблон вызывает шаблон inputTmpl для создания элементов input. Вызов второго шаблона осуществляется с помощью дескриптора {{tmpl}} . В этом вызове используются три аргумента. Первые два - это текущий элемент данных и объект options; эти аргументы заключаются в круглые скобки. Третий аргумент - это вызываемый шаблон. Его можно задавать либо jQuery-селектором (что и сделано выше), либо переменной или функцией, определенной в сценарии.

    Использование условных шаблонов

    Механизм шаблонов обеспечивает возможность динамического принятия решений относительно использования различных частей шаблона в зависимости от выполнения определенных условий. Для этого существуют дескрипторы {{if}} и {{/if}} , пример использования которых представлен в ниже:

    ... {{if stocklevel > 0}} .png"/> ${name}: {{/if}}

    Условие указывается в дескрипторе {{if}}, и часть шаблона, заключенная между этим дескриптором и дескриптором {{/if}}, будет использоваться, только если результат вычисления условного выражения окажется равным true. Если же этот результат равен false, то указанная часть шаблона игнорируется. В данном случае проверяется значение свойства stocklevel, и если оно равно нулю, то игнорируется весь шаблон flowerTmpl. Это означает, что отображаться будут лишь те продукты, которые имеются в наличии на складе, как показано на рисунке:

    Более сложные условия можно задавать с помощью дескриптора {{else}} , позволяющего определить часть шаблона, которая должна использоваться в тех случаях, когда результатом вычисления выражения в дескрипторе {{if}} является false. Соответствующий пример приведен ниже:

    ... {{if stocklevel > 5}} .png"/> ${name}: {{else stocklevel > 0}} .png"/> ${name}: (Небольшое количество) {{else}} .png" style="opacity:0.5"/> ${name} (Нет в наличии) {{/if}}

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

    Результат работы приведенного выше сценария представлен на рисунке: