jQuery Templates, czyli szablony zawitały do jQuery

Przed kilkoma miesiącami na oficjalnym blogu jQuery pojawiła się informacja na temat powstania trzech nowych pluginów, które zostały zaakceptowane jako oficjalne rozszerzenia jQuery. Były to jQuery Templates, jQuery Data Link oraz jQuery Globalization. Według podanej wówczas informacji jQuery Templates miał trafić do głównej biblioteki jQuery wraz z pojawieniem się wersji 1.5. Najwyraźniej ekipa pracująca nad tym pluginiem nie zdążyła na czas i w wydanej kilka dni temu bibliotece, nie uraczymy szablonów. Nic nie stoi jednak na przeszkodzie, aby pobrać ten plugin osobno i już dzisiaj zacząć poznawanie jego możliwości.

Instalacja

Instalacja plginu jest tak samo banalna jak instalacja samego jQuery i sprowadza się do pobrania jego najnowszej wersji ze strony https://github.com/jquery/jquery-tmpl. Następnie wystarczy dołączyć plugin do strony.

Pierwszy szablon

Zanim zaczniemy zagłębiać się w szczegóły szablonów, rzućcie okiem na poniższy kod.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<script src="jquery-1.5.min.js"></script>
		<script src="jquery.tmpl.min.js"></script>
		<script id="szablon" type="text/x-jquery-tmpl">
			<tr>
				<td>${nazwa}</td>
				<td>${cena}zł</td>
				<td><a href="/usun?id=${id}">usuń</a></td>
			</tr>
		</script>
		<script>
		$(document).ready(function() {
			var produkty = [
				{ id: 1, nazwa: "Spodnie", 	cena: "100" },
				{ id: 2, nazwa: "Buty", 	cena: "250" },
				{ id: 3, nazwa: "Okulary", 	cena: "90" },
				{ id: 4, nazwa: "Worek", 	cena: "1" }
			];

			$("#szablon").tmpl(produkty).appendTo("#produkty tbody");
		});
		</script>
	</head>
	<body>
		<table id="produkty">
			<thead>
				<tr>
					<th>Nazwa</th>
					<th>Cena</th>
					<th>Akcja</th>
				</tr>
			</thead>
			<tbody>
			</tbody>
		</table>
	</body>
</html>

To co widzicie to najprostszy przykład zastosowania szablonów jQuery. Wszystko sprowadza się do stworzenia szablonu HTML w znaczniku script, któremu nadajemy identyfikator (w tym przypadku identyfikatorem jest słowo szablon). Oprócz identyfikatora znacznik ten musi posiadać odpowiedni typ. Jest nim text/x-jquery-tmpl. W przypadku problemów z przeglądarkami, które nie będą poprawnie interpretować tego atrybutu można stosować text/html.

Tagi szablonów oraz nowe metody

W chwili obecnej do jQuery wprowadzone zostały następujące tagi.

  • ${} lub ${{= }} – służy do wstawiania do szablonu danych tekstowych
  • {{html }} – służy do wstawienia danych w postaci html
  • {{if}}, {{else}}, {{/if}} – podstawowa instrukcja warunkowa
  • {{each}} – pętla
  • {{tmpl}} – renderowanie szablonu w szablonie
  • {{wrap}} – możliwość opakowania jednego szablonu innym

Oprócz tagów, do jQuery wprowadzone zostały nowe funkcje. Są to:

  • .tmpl() – metoda wywoływana na szablonie. Przyjmuje dwa parametry – dane oraz opcje. Dane mogą zostać przekazane na dwa sposoby. W pierwszym z nich będzie to obiekt, którego klucze będą stanowić zmienne dostępne w szablonie. W drugim sposobie można przekazać tablicę obiektów. Wówczas szablon zostanie wyrenderowany tyle razy, ile elementów będzie miała tablica. Opcje służą do przekazania do szablonu dodatkowych danych. W wyniku działania tej metody uzyskamy kolekcję elementów, które możemy wstawić do drzewa DOM przy pomocy metod .appendTo, .prependTo, .insertBefore oraz .insertAfter.
  • jQuery.tmpl() – działa na podobnej zasadzie jak poprzednia metoda, z tą różnicą, że pierwszym argumentem jest treść szablonu, drugim dane, a trzecim opcje.
  • .tmplItem() oraz jQuery.tmplItem() – metody, dzięki którym uzyskamy dostęp do danych wyrenderowanego elementu na podstawie szablonu oraz wszystkich informacji powiązanych z tym elementem
  • .template() oraz jQuery.template() – metody te umożliwiają na “skompilowanie” szablonu do postaci nazwanego szablonu (named template), do którego możemy się odwoływać po jego nazwie.

Ponieważ w teorii wszystko zawsze wydaje się być bardziej skomplikowane niż to jest w rzeczywistości, przejdźmy do przykładów.

${}

W miejsce tego taga wstawiona zostanie zawartość przypisana pod klucz obiektu przekazanego jako dane do szablonu.

<script id="szablon" type="text/x-jquery-tmpl">
	<p>${pole}</p>
</script>
<script>
$(document).ready(function() {
	$("#szablon").tmpl({ pole: "zawartosc"}).appendTo("#element");
});
</script>
<div id="element"></div>

Zamiast ${pole} możemy napisać {{= pole}} i efekt będzie identyczny.

{{html}}

Działa identycznie jak poprzedni tag, z tą różnicą, że pozwala na osadzanie w szablonie kodu HTML

<script id="szablon" type="text/x-jquery-tmpl">
	<p>${pole}</p>
</script>
<script>
$(document).ready(function() {
	$("#szablon").tmpl({ pole: "<b>zawartosc</b>"}).appendTo("#element");
});
</script>
<div id="element"></div>

{{if}}, {{else}}, {{/if}}

W przypadku instrukcji warunkowej nie możemy korzystać z zapisu ${jakasNazwa}. Zamiast tego odwołujemy się do danych bezpośrednio przy pomocy nazwy właściwości obiektu.

<script id="szablon" type="text/x-jquery-tmpl">
	${ilosc} to
	{{if ilosc < 10}}
		za malo
	{{else ilosc == 10}}
		w sam raz
	{{else}}
		za duzo
	{{/if}}
	<br />
</script>
<script>
$(document).ready(function() {
	var dane = [
		{ ilosc: 5 },
		{ ilosc: 10 },
		{ ilosc: 12 }
	];
	$("#szablon").tmpl(dane).appendTo("#element");
});
</script>
<div id="element"></div>

{{each}}

Jeśli w obiekcie, który przekazujemy do szablonu znajdzie się tablica, wówczas możemy po niej iterować przy pomocy taga {{each}}. Dane w pętli reprezentowane są przez zmienną $value.

<script id="szablon" type="text/x-jquery-tmpl">
	<li>
	${nazwa}<br />
	dostepne rozmiary: {{each rozmiary}}${$value} {{/each}}
	</li>
</script>
<script>
$(document).ready(function() {
	var dane = [
		{ nazwa: "Spodnie", rozmiary: ["S", "M", "L"] },
		{ nazwa: "Koszula", rozmiary: ["S", "M"] }
	];
	$("#szablon").tmpl(dane).appendTo("#element");
});
</script>
<ul id="element"></ul>

Jak na porządną pętle przystało, {{each}} daje dostęp do wewnętrznego licznika.

<script id="szablon" type="text/x-jquery-tmpl">
	<li>
	${nazwa}<br />
	dostepne rozmiary:<br />
	{{each(i, symbol) rozmiary}} ${i + 1}: ${symbol}<br />{{/each}}
	</li>
</script>
<script>
$(document).ready(function() {
	var dane = [
		{ nazwa: "Spodnie", rozmiary: ["S", "M", "L"] },
		{ nazwa: "Koszula", rozmiary: ["S", "M"] }
	];
	$("#szablon").tmpl(dane).appendTo("#element");
});
</script>
<ul id="element"></ul>

{{tmpl}}

Renderowanie szablonu w szablonie jest bardzo pomocną techniką w przypadku powtarzających się elementów, występujących w głównym szablonie. Dane w dołączonym szablonie znajdują się w obiekcie $item, a dokładniej w jego właściwości data.

<script id="szablon" type="text/x-jquery-tmpl">
	<li>
	${nazwa}<br />
	dostepne rozmiary: {{tmpl(rozmiary) "#rozmiary_tmpl"}}
	</li>
</script>

<script id="rozmiary_tmpl" type="text/x-jquery-tmpl">
	<p>${$item.data}</p>
</script>

<script>
$(document).ready(function() {
	var dane = [
		{ nazwa: "Spodnie", rozmiary: ["S", "M", "L"] },
		{ nazwa: "Koszula", rozmiary: ["S", "M"] }
	];
	$("#szablon").tmpl(dane).appendTo("#element");
});
</script>
<ul id="element"></ul>

{{wrap}}

Tag {{wrap}} działa na podobnej zasadzie jak {{tmpl}}, z tą różnicą, że zamiast wstawiać szablon w konkretne miejsce, otacza dane naszym kodem. Jest to niezmiernie przydatne w przypadku odbierania danych w postaci HTML, na wygląd których nie mamy wpływu.

<script id="szablon" type="text/x-jquery-tmpl">
	{{wrap "#wrapper"}}
		<p>${nazwa}</p>
	{{/wrap}}
</script>

<script id="wrapper" type="text/x-jquery-tmpl">
	{{each $item.html("p")}}
		<b>{{html $value}}</b>
	{{/each}}
</script>

<script>
$(document).ready(function() {
	var dane = [
		{ nazwa: "Spodnie" },
		{ nazwa: "Koszula" }
	];
	$("#szablon").tmpl(dane).appendTo("#element");
});
</script>
<div id="element"></div>

Po co nam szablony w jQuery?

Zastosowań szablonów w jQuery jest co najmniej kilka. Przede wszystkim zastąpią sklejanie kodu HTML ze zmiennymi JavaScript i wstawianie go do treści strony. Ktoś zaraz powie, że przecież można pracować bezpośrednio na DOM i dodawać do drzewa nowe węzły. Można, ale każdy, kto to robił wie, ile jest przy tym roboty. Szablony dają nam czysty i relatywnie prosty mechanizm. Najczęściej będą spotykane w połączeniu z AJAXem, gdzie dane odbierane są jako XML lub JSON.

Ciemne strony szablonów

Podczas poznawania szablonów starałem znaleźć się ich wady i szczerze przyznam, że trochę się tego uzbierało. Do wad można zaliczyć:

  • Nowa niezbyt intuicyjna składnia.
  • Problemy z debugowaniem kodu.
  • Każdy znak jest ważny, nawet spacja, która postawiona w niewłaściwym miejscu powoduje, że szablony nie działają, a JavaScript zgłasza nic nie mówiący błąd.
  • Przy dużej ilości szablonów w kodzie powstaje bałagan. Teoretycznie szablony można dołączać jako osobne pliki, jednak nie udało mi się znaleźć dokładnych informacji na ten temat.
  • Niezbyt dobra dokumentacja. Bardziej skomplikowane szablony wymagają zastosowania metody prób i błędów.
  • Wprowadzenie nowego rodzaju skryptu – text/x-jquery-tmpl.
  • Obawiam się o wydajność tego rozwiązania w przypadku dużej ilości danych.

Co dalej?

Leave a comment ?

17 Comments.

  1. Jakiś czas temu zastanawiałem się nad użyciem tych szablonów, ale wymagałoby to w moim przypadku zrobienia konwertera z szablonów smarty (albo w drugą stronę js-tpl -> smarty-tpl).

    Z drugiej strony, szablony te mają sens tylko, jeżeli są częścią czegoś większego. Samo zastępowanie zmiennych (nawet w pętli) w JS robi się bez większego problemu…

  2. Wszystko ma na celu przerzucenie renderowania widoków na stronę klienta – ostatnio o czymś podobnym pisał cojack. Moim zdaniem, tradycyjnie jest to ciekawa nowość, aczkolwiek wprowadza nowe problemy związane z bezpieczeństwem kanałów przesyłania danych. No, chyba, że ma to jedynie służyć wyświetleniu danych wyrzuconych przez PHP. ;]

  3. Kiedyś radziłem sobie za pomocą ukrytych ukrytych div’ów i prostego mechanizmu tak jak tutaj:
    http://vimeo.com/736030
    Długo czekałem na sensowne rozwiązanie dotyczące szablonów w jQuery. Jednak umieszczanie ich w sekcji wygląda bardzo nieładnie. To się validuje w ogóle?

    Trochę mnie dziwi, że to rozwiązane zostało oficjalnie przyjęte do projektu jQuery, ale jak widać polityka wzięła górę i jQuery zostało oczarowane berłem Microsoftu w postaci tego dodatku.

  4. @Tomasz Kowalczyk
    Dzięki szablonom w JavaScript można przesyłać AJAXem wyłącznie dane, bez ich prezentacji, tak jak ma to miejsce obecnie. Bardzo częstym rozwiązaniem jest zwracanie w odpowiedzi kodu HTML, a następnie umieszczanie go gdzieś na stronie. Jednak przeniesienie ciężaru całego procesu renderowania warstwy prezentacji na tak awaryjną technologię, jest kiepskim pomysłem.

    @DrLex
    Jeśli dodasz do pierwszego przykładu znacznik title, to walidator będzie zielony jak trawnik na wiosnę. Poza tym zawsze można zmienić type na text/html.
    Dlaczego rozwiązanie to (oraz Data Link) nie miały zostać przyjęte do biblioteki jako jej część? Tylko dlatego, że stworzył je Microsoft? Jeśli autorem byłby Apple/Google/Facebook, to byłyby lepsze?

  5. Kiedyś też spotkałem się z potrzebą użycia takich szablonów. Też wykorzystałem ukryte kontenery, a taka funkcjonalność mi się marzyła.

    Jednak to rozwiązanie bardzo mi nie pasuje i raczej będę dalej wykorzystywał ukryte kontenery.

    @batman: Umieszczanie kodu (x)HTML w ? Apple/Google czy Facebook raczej by na ten „genialny pomysł” nie wpadł. Wygląda to tragicznie, a przy większym projekcie burdel murowany.

  6. Ten znak zapytania „?” miał być tagiem head, ale zamiast zamienić > i < na &lt/&gt twój system komentarzy go wywalił.

  7. @strach
    Szablon umieściłem w head z rozpędu. Nic nie stoi na przeszkodzie, aby umieścić go w dowolnym innym miejscu strony, np tuż przed zamknięciem znacznika body. Jak już pisałem nie udało mi się znaleźć sposobu na tworzenie szablonów w osobnych plikach. Odnoszę jednak wrażenie, że będzie to pierwsza poprawka, jaka zostanie dodana do jQuery Templates.

  8. @DrLex
    ” Trochę mnie dziwi, że to rozwiązane zostało oficjalnie przyjęte do projektu jQuery, ale jak widać polityka wzięła górę i jQuery zostało oczarowane berłem Microsoftu w postaci tego dodatku. ”

    co przez to rozumiesz? jquery template nie zostało włączone do głównej biblioteki, a działa jak zwykła wtyczka/plugin, jakich wiele.

    luknij na źródło jquery 1.5, widzisz gdzieś tam metodę tmpl? http://code.jquery.com/jquery-1.5.js

    @batman
    zapomniałeś wspomnieć o największej wadzie takiego rozwiązania – SEO.

    niemniej w niektórych przypadkach można oprzeć całą aplikację na js, jak to w dużej mierze robi np. facebook. tym sposobem można zaoszczędzić sporo pamięci ;)

  9. @Kamil chodziło mi o to, że akurat ta koncepcja template’ów stała się wiodąca i „promowana” kiedy to dużo bardziej eleganckie rozwiązanie można znaleźć chociażby tutaj:
    http://ivorycity.com/blog/jquery-template-plugin/
    Co do SEO, to Google jasno wyraziło się na temat indeksowania stron opartych o AJAX. Należy zapewnić alternatywną treść generowaną po stronie serwera analogiczną do tej po stronie frontendu.
    Wiąże się to z aktualizacją urli np. poprzez stosowanie anchorów i przygotowania stosownej logiki po stronie serwera (czyli 2x więcej roboty). Pod względem użyteczności też to ma ogromne znaczenie, tak aby być w stanie dać komuś link do dynamicznie wygenerowanych po stronie frontendu treści.

  10. Fajny plugin do jQuery, ale jak dla mnie to już trochę przerost formy nad treścią. Sprawdzał ktoś z Was wydajność takiego kodu?

  11. Rozwiązanie ciekawe. W wielu miejscach ułatwi pakowanie JSON-a na stronę, zamiast „łapać” poszczególne elementy i dodawać elementy ręcznie.
    W bardziej zaawansowanych przypadkach jeszcze się nie sprawdzi.

  12. @Kamil
    Jeszcze nie zostało wrzucone do głównej biblioteki. Według informacji sprzed kilku miesięcy, plugin ten znajdzie się w jQuery.

    @DrLex
    To co podesłałeś, niewiele się różni od jQuery Templates.

    @shpyo
    Wydajności nie sprawdzałem, ale nie sądzę, by coś, co miało zamulać, trafiło do jQuery.

    @matipl
    Bałbym się opierać layout strony o szablony JavaScript. Wystarczy jeden błąd w js i cała strona leży. Jest tak jak piszesz. jQuery Templates idealnie nada się do bezproblemowego opakowania danych przylatujących do nas np. z AJAXa.

  13. Wszystko zależy od kontekstu. Klient pocztowy w przeglądarce to wyłącznie JS ;)

  14. Masz rację. O tym nie pomyślałem.
    Sprawdziłem przed chwilą Gmaila bez JavaScrpit i na dzień dobry wyświetlił się komunikat o braku włączonego js oraz link do wersji HTML. Czyli jak ma się czas i zasoby ludzkie, to można zrobić dwie wersje aplikacji :)

  15. Czas – magiczne słowo :)
    gmail, poczta.onet.pl – teraz wszędzie bez JS ani rusz…

  16. Znalazłem tutaj sposób na ładowanie templatek z zewnętrznych plików – może się przyda :)

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackbacks and Pingbacks:

Subscribe without commenting