Nette Framework: AJAX (pokračování)

Po seznámení se základy používání AJAXu v rámci frameworku Nette nastal čas podívat se na tvorbu interaktivních AJAXových aplikací do větší hloubky. Naučíme se pořádně pracovat s odpovědí serveru. A vyzkoušíme si tvorbu jednoduchého našeptávače.
Seriál: Začínáme s Nette Framework (17 dílů)
- Nette Framework: zvyšte svoji produktivitu 10. 3. 2009
- Nette Framework: Odvšivujeme 17. 3. 2009
- Nette Framework: MVC & MVP 24. 3. 2009
- Nette Framework: Refactoring 31. 3. 2009
- Nette Framework: Chytré šablony 7. 4. 2009
- Nette Framework: adresářová struktura aplikace 14. 4. 2009
- Nette Framework: AJAX 21. 4. 2009
- Nette Framework: AJAX (pokračování) 28. 4. 2009
- Nette Framework: AJAX (dokončení) 5. 5. 2009
- Nette Framework: Sessions 12. 5. 2009
- Nette Framework: Přihlašování uživatelů 19. 5. 2009
- Nette Framework: Ověřování oprávnění a role 26. 5. 2009
- Nette Framework: Neprůstřelné formuláře 2. 6. 2009
- Nette Framework: Neprůstřelné formuláře II 9. 6. 2009
- Nette Framework: Neprůstřelné formuláře III 16. 6. 2009
- Nette Framework: Cache 23. 6. 2009
- Nette Framework: Co se do seriálu nevešlo? 30. 6. 2009
Nálepky:
V předchozí části seriálu o Nette Framework jsme nakousli téma fragmentů HTML, označovaných ve frameworku jako snippety. Jde o bloky, které kodér označí v HTML šabloně párovou značkou {snippet}…{/snippet}
a framework se už sám postará o to, aby při AJAXovém požadavku přenášel jen jejich obsah. Je poté na javascriptovém handleru, aby obsah snippetů vložil do aktuální stránky.
Protože snippetů může být ve stránce více, je možné je pojmenovat. Takto například vytvoříme snippet s názvem counter
:
{snippet counter}
Počet hlasů: {$count}
{/snippet}
Pozn.: pokud by to prospělo přehlednosti, je možné název vložit i do koncové značky.
Při běžném, tj. neAJAXovém, požadavku vygeneruje šablona tento HTML kód:
<div id="__counter">Počet hlasů: 23</div>
Jak vidíte, snippety se obalí do elementu div
, jehož id
tvoří název snippetu doplněný o dvě podtržítka. To aby se minimalizovala kolize s jinými id
ve stránce.
Pokud byste chtěli div
nahradit za jiný element, je možné jej specifikovat jako druhý parametr za názvem:
{snippet counter p}
Počet hlasů: {$count}
{/snippet}
Vygeneruje:
<p id="__counter">Počet hlasů: 23</p>
Toliko k běžnému požadavku. Nyní se naopak podívejme, jaká data se přenášejí při AJAXovém požadavku:
{"snippets":{"__counter":"Počet hlasů: 23"}}
Výstupem je datová struktura, označovaná jako payload, serializovaná do JSON řetězce. V tomto případě server vrátil asociativní pole (v řeči JavaScriptu je přesnější termín objekt) s jedním prvkem snippets
obsahujícím pole všech přenášených snippetů uložených jako pár ID a HTML kód.
Aktualizaci HTML stránky lze pak provést například tímto skriptem, který předpokládá přítomnost frameworku jQuery a proměnné payload
:
for (var id in payload.snippets) {
$("#" + id).html(payload.snippets[id]);
}
Které snippety přenášet?
Šablona může obsahovat spoustu snippetů. Jelikož Nette Framework je komponentově orientovaný, mohou se na stránce nacházet komponenty, jejichž šablony také obsahují snippety (přičemž samotný presenter není nic jiného, než speciální komponenta). Aby se při každém požadavku nepřenášely úplně všechny snippety, existuje ve frameworku mechanismus, jak určit, které přenést a které naopak vynechat. V řeči frameworku jde o odlišení neplatných a platných snippetů (neplatné se přenášejí). Ke zneplatnění slouží metoda invalidateControl(), jejíž volitelný parameter je název snippetu. Pokud parametr neuvedeme, znamená to, že se zneplatní celá komponenta a tedy všechny snippety. Obdobně pro validaci existuje metoda validateControl().
Příklad použití:
public function renderDefault()
{
$this->template->count = 23;
$this->invalidateControl('counter'); // snippet 'counter' se přenese
}
Nejen snippety živ je AJAX
Hned z kraje musím zmínit, že snippety představují jen vysokoúrovňový mechanismus, který vůbec nemusíte používat. To znamená, že můžete na výstup poslat jakýkoliv řetězec, ať už je to obyčejný HTML kód nebo serializovaná datová struktura. Přičemž pro datové struktury Nette Framework nabízí speciální podporu. Podívejme se na ni blíže.
Součástí presenteru je datové úložiště $presenter->payload
(pozn. ve starších verzích frameworku bylo dostupné metodou getAjaxDriver()
). Do tohoto úložiště můžete zapisovat jakákoliv data a presenter je nakonec pošle na výstup serializované, standardně jako JSON.
public function renderDefault()
{
$this->payload->count = 23;
$this->payload->show = TRUE;
$this->terminate(); // ukončí presenter
}
Vygeneruje:
{"count":23,"show":true}
Ano, přesně tohle úložiště využívají i snippety, zapisují svůj obsah do $presenter->payload->snippets[$id] = $html
. Samozřejmě lze zároveň používat snippety i zapisování uživatelských dat do $this->payload
. Framework odešle na výstup vše.
Našeptávač
Přikladem samostatného použití $presenter->payload
může být velmi jednoduchý našeptávač:
class AutoCompletePresenter extends Presenter
{
public function handleAutoComplete($text)
{
$this->payload->autoComplete = array();
$text = trim($text);
if ($text !== '') {
// načteme seznam států (pro jednoduchost ze souboru)
$list = file(dirname(__FILE__) . '/items.txt');
// vytvoříme seznam pro našeptávač
foreach ($list as $item) {
$item = trim($item);
if (strncasecmp($item, $text, strlen($text)) === 0) {
$this->payload->autoComplete[] = $item;
}
}
}
// činnost presenteru tímto můžeme ukončit
$this->terminate();
}
....
}
V šabloně bude textové políčko a na něj zavěsíme handler pro událost onkeyup. Ta zavolá výše uvedenou metodu pomocí {link autoComplete!}
a předá ji obsah políčka v parametru text
. Od serveru získá pole payload.autoComplete
, ze kterého seznam.
<p>Název státu: <input type="text" id="text" /></p>
<script type="text/javascript">
<!--
$('#text').focus().keyup(function(event) {
$.getJSON({link autoComplete!}, {'text': $('#text').val()}, function(payload) {
$('ul').remove();
var list = $('<ul></ul>').insertAfter('#text');
for (var i in payload.autoComplete) {
$('<li></li>').text(payload.autoComplete[i]).appendTo(list);
}
});
});
-->
</script>
Pokračování příště
Tím jsme s AJAXem ještě úplně neskončili. Bude mu věnovaný i příští díl.
Autor článku je vývojář na volné noze, specializuje se na návrh a programování moderních webových aplikací. Pravidelně pořádá školení pro tvůrce webových aplikací, vyvíjí open-source knihovny Texy, dibi a Nette Framework.
Čekal jsem, že tenhle seriál bude dobrý, ale tohle předčilo očekávání!
koukam, ze vyvoj frameworku jde vzdy ruku v ruce se serialem na rootu, kdyz jsem rozbalil aplikaci na serveru, tak nejela… a ono vcera vyslo nove nette… :-)
přesně tak :-) novinkou je metoda $presenter->getPayload()
http://forum.nettephp.com/cs/1649-prejmenovat-getajaxdriver?pid=10155
super článek :-)
"Invalidní = musí se přenášet" je asi první vtip ve frameworku, který jsem kdy viděl :) +1
jak moc (nebo nijak:) je s Nette svazano jQuery. Rekneme, ze bych ho chtel nahradit Mootools, protoze se mi ho nechce pouzivat?
Vůbec není na jquery ani na jiný JS Framework vázané. Je to Vaše volba.
Chápu, že to v kódu není uvedeno kvůli přehlednosti, ale neodpustím si poznámku, že JavaScriptová obsluha by měla respektovat asynchronnost AJAXu. Jde o to, že než mi připluje odpověď ze serveru, může se hodnota v políčku změnit.
Kromě této chyby by se dal kód samozřejmě dále vylepšit třeba tak, aby server nebombardoval požadavky častěji než je nutné, ale to už je skutečně nad rámec tohoto článku.
nechci stourat, ale vyraz invalidni neni totez co v angl. invalidate. Zni to jako neco co neni validni a ne jako neco cemu skoncila platnost (z duvodu aktualizace dat), lepsi mi prijde napr. zneplatneny (odvozene od zrusit platnost – invalidate)
To je pravda, ten překlad by chtěl vylepšit. Pokusím se to v článku změnit.
Tohle by možná ještě chtělo rozmyslet. Je pravda, že slovo invalidace se v tomto kontextu používá (např. Flex má interface IInvalidating [1]), nevýhodou ale je, že opakem je termín "validace", který se používá v úplně jiném významu. Například tvoje validateControl() ve mně evokuje běžnou validaci, nic s AJAXem.
Otázka je, co místo toho. Napadlo mě třeba markForRefresh(), forceRefresh() nebo setDirty(), ale určitě by se dalo přijít i na něco lepšího.
[1] http://livedocs.adobe.com/flex/3/langref/mx/core/IInvalidating.html
P.S. Ale stejně, "ivalidní, musí se tedy přenášet" se mi moc líbilo :)
Zrovna teď jsem narazil na Windows
API, kde mě „potěšilo“, že k Invalidate jako opak používají taky
Validate :-)
Neviděl bych v tom problém. Tahle metoda se stejně moc nepoužívá a na
vizuálních komponentách obvykle není co validovat ve smyslu, v jakém se
validují třeba formuláře.
Ahoj,
nepochopil jsem vyznam $this->terminate() – v tomto pripade je script ukoncen a server odpovi s prazdnymi daty. Dokud jsem dany radek nezakomentoval, tak se nezobrazovaly vysledky naseptavace.