Nette Framework: AJAX

AJAXové aplikace jsou populární, neboť poskytují uživatelsky příjemnější prostředí. Pro programátory je však AJAX často noční můrou (nemluvě o tom, že špatná AJAXová aplikace je noční můrou pro uživatele). V následujících dílech se proto podíváme, jak správně psát AJAXové aplikace za použití Nette Framework.

Seriál: Začínáme s Nette Framework (17 dílů)

  1. Nette Framework: zvyšte svoji produktivitu 10.3.2009
  2. Nette Framework: Odvšivujeme 17.3.2009
  3. Nette Framework: MVC & MVP 24.3.2009
  4. Nette Framework: Refactoring 31.3.2009
  5. Nette Framework: Chytré šablony 7.4.2009
  6. Nette Framework: adresářová struktura aplikace 14.4.2009
  7. Nette Framework: AJAX 21.4.2009
  8. Nette Framework: AJAX (pokračování) 28.4.2009
  9. Nette Framework: AJAX (dokončení) 5.5.2009
  10. Nette Framework: Sessions 12.5.2009
  11. Nette Framework: Přihlašování uživatelů 19.5.2009
  12. Nette Framework: Ověřování oprávnění a role 26.5.2009
  13. Nette Framework: Neprůstřelné formuláře 2.6.2009
  14. Nette Framework: Neprůstřelné formuláře II 9.6.2009
  15. Nette Framework: Neprůstřelné formuláře III 16.6.2009
  16. Nette Framework: Cache 23.6.2009
  17. Nette Framework: Co se do seriálu nevešlo? 30.6.2009

Aby nedošlo k nedorozuměním, definujme si nejdřív, co je to vlastně AJAX je. Jde o skupinu technologií sloužících k vytváření interaktivních webových aplikací, které mohou komunikovat se serverem bez nutnosti znovunačtení stránky. Navzdory názvu Asynchronous JavaScript and XML se nevyžaduje ani JavaScript, ani XML, ani asynchronnost.

Zatímco samotná komunikace se serverem nepředstavuje zásadnější problém, objevují se jiné komplikace:

  • jak reprezentovat aktuální stav aplikace v adresním řádku
  • jak reagovat na tlačítka Zpět, Vpřed a pohyb v historii navštívených stránek
  • jak se vyhnout závislosti na JavaScriptu
  • jak uživateli signalizovat, že aplikace zpracovává jeho požadavek
  • udržet pod kontrolou zátěž serveru; AJAX může snížit zátěž na webové servery, ale může zvýšit počet HTTP požadavků

Zatímco pro některé úkoly již existují zavedené návrhové vzory (zpracování se signalizuje známým animovaným GIFem, stav aplikace se zaznamenává do fragmentu webové adresy), pro jiné je třeba použít různé triky a hacky. Do této kategorie patří zejména odchytávání stisknutí tlačítek Zpět či Vpřed nebo procházení historií, kde s čistým řešením přichází teprve HTML5.

JavaScript a PHP hrají ping-pong

Rozdíl v programování klasické a AJAXové aplikace je v zapojení nové vrstvy napsané v JavaScriptu do hry. Zatímco v klasické aplikaci kliknutí na odkaz automaticky vyvolá požadavek na server a vrácená stránka se automaticky zobrazí, v případě AJAXových aplikací požadavek vyvolá a vrácená data zpracuje klientský skript. Nazývejme takový požadavek AJAXovým. Přičemž z pohledu webového serveru jde stále o normální GET nebo POST požadavek HTTP.

Jinými slovy:

  1. JavaScript zformuluje interakci uživatele do GET nebo POST požadavku a zašle na server
  2. PHP odpoví
  3. JavaScript dekóduje odpověď a provede reakci

JavaScript tak hraje s PHP ping-pong a má vždy výhodu podání. Kromě toho by měl zobrazovat a skrývat obrázek signalizující zpracování požadavku a může také aktualizovat stav uložený ve fragmentu webové adresy.

Nejprve se pod lupou podíváme na druhou fázi pohybu ping-pongového míčku – jak může vypadat odpověď serveru?

1) HTML stránka

AJAXovou aplikaci je dokonce možné postavit i nad serverem, který odmítá spolupráci. Stačí, že vrátí celou HTML stránku, a je to obslužný JavaScript, kdo z ní vytáhne potřebné informace (obvykle elementy HTML, které pak zapracuje do aktuální stránky v prohlížeči a zbytek zahodí). Přesně tohle dělá funkce load z frameworku jQuery.

2) HTML fragment

Předchozí postup je sice snadný, ale poměrně neefektivní. Server musí třeba vygenerovat složitou a objemnou stránku, ze které se použije jen zlomek a zbytek se zahodí. Vhodnější je proto dopsat na straně serveru podporu, která bude umět vrátit jen tu část stránky, která se změnila. Tedy jakýsi fragment HTML.

3) JSON nebo XML

Co kdyby došlo k více změnám na různých místech stránky? V takovém případě je potřeba předat fragmentů HTML víc. Například ve formě asociativního pole, kde klíče budou ID elementů a hodnoty jejich HTML kódy. Na to ovšem potřebujeme složitější struktury. Třeba JSON nebo XML.

ČTĚTE K TÉMATU: JSON pro výměnu dat na webu

Obojí jsou standardizované formáty pro výměnu dat. Oba se na straně JavaScriptu i PHP snadno čtou i generují (i když práce s JSON na straně JavaScriptu má svá úskalí). A nemusí sloužit pouze k předání HTML fragmentů, lze jít dál a zcela opustit vrstvu HTML. Je možné předávat data, ze kterých teprve samotný JavaScript vygeneruje HTML kód nebo bude manipulovat s aktuální stránkou.

4) Žádný obsah

Aby byl výčet kompletní, je třeba uvést i situaci, kdy server žádný obsah nepošle. Znáte ty hlasovací otázky Líbil se vám článek? na blozích? Pomocí AJAXu zaznamenávají kliknutí, server inkrementuje čítač v databázové tabulce, ale už nemá co odpovědět.

Zajímavostí je, že tento typ AJAXového požadavku lze realizovat dokonce bez klientského skriptování. Stačí obyčejný hypertextový odkaz, na který server odpoví HTTP kódem 204 No Content. Podle specifikace poté nesmí prohlížeč znovunačíst stránku, což také browsery dodržují.

Nicméně i v tomto případě je záhodno připojit klientský skript, který odkaz nahradí např. oznámením Váš hlas byl zaznamenán. Jinak bude uživatel zmaten a bude opakovaně klikat na odkaz s očekáváním nějaké reakce.

AJAX a Nette Framework

Nette Framework podporuje všechny ping-pongové míčky, tedy všechny typy komunikace. Nenutí vás používat žádnou konkrétní javascriptovou knihovnu, takže si můžete napsat vlastní obslužný skript, můžete využít jQuery atd. Framework podporuje a velmi zjednodušuje zasílání fragmentů HTML. A vede programátora k tvorbě aplikací, které plně fungují i bez JavaScriptu.

Možná budete až překvapeni, jak snadno naučíme Automat na kávu AJAX.

Navrhoval bych využít javascriptový framework jQuery. Můžete samozřejmě namítnout, že k obsluze jednoduchého AJAXového požadavku je zbytečné natahovat celý framework, a budete mít pravdu. Jenže v praxi obvykle u samotné komunikace se serverem nezůstane, je potřeba provádět různé kejkle s dokumentem a bavit uživatele vizuálními efekty, ke kterým framework už potřeba je. S tímto vědomím ho použijeme i nyní. A společně s ním i Nette Framework plugin pro jQuery, jehož autorem je Jan Marek.

Plugin obslouží zbývající dvě fáze ping-pongové hry. Tou první je odeslání reakce uživatele na server prostřednictví AJAXového požadavku. Slovy kodéra to znamená zavěsit na kotvy <a href="..."> obsluhu události onclick, která operace vykoná a vrátí false, aby prohlížeč nenačetl další stránku.

<a href="{link insert!, 1}" onclick="obsluznyHandler(); return false">
  <img src="images/coin-1.png" alt="Vhoď 1 Kč" />
</a> 

Existuje však elegantnější způsob, známý jako unobtrusive JavaScript. Spočívá v tom, že obsluhu událostí zavěsíme pomocí skriptu. Například na všechny kotvy, které mají třídu ajax. Takže v šablonách bude jen:

<a href="{link insert!, 1}" class="ajax">
  <img src="images/coin-1.png" alt="Vhoď 1 Kč" />
</a> 

A následující kód, který najdete v souboru jquery.nette.js, všechny kotvy „zAJAXovatí“.

$(function() {
        // nastaví událost onclick pro všechny elementy A s třídou 'ajax'
        $("a.ajax").live("click", function(event) {
                $.get(this.href); // zahájí AJAXový požadavek

                // zobrazí spinner, signalizující uživateli, že se něco děje
                $('<div id="ajax-spinner"></div>').css({
                        position: "absolute",
                        left: event.pageX + 20,
                        top: event.pageY + 40

                }).ajaxStop(function() {
                        $(this).remove(); // po skončení spinner smaž

                }).appendTo("body");

                return false;
        });
}); 

Třetí a poslední fází ping-pongu je zpracování odpovědi vrácené serverem. Skript jquery.nette.js tím pověří handler  jQuery.netteCallback:

jQuery.extend({
        updateSnippet: function(id, html) {
                $("#" + id).html(html);
        },

        netteCallback: function(data) {
                // přesměrování
                if (data.redirect) {
                        window.location.href = data.redirect;
                }

                // snippety
                if (data.snippets) {
                        for (var i in data.snippets) {
                                jQuery.updateSnippet(i, data.snippets[i]);
                        }
                }
        }
});


jQuery.ajaxSetup({
        success: function (data) {
                jQuery.netteCallback(data);
        },

        dataType: "json"
}); 

Handler očekává JSON strukturu, která obsahuje buď příkaz pro přesměrování, nebo pole fragmentů HTML (ve frameworku nazývaných snippety), které vloží na příslušná místa aktuální stránky.

Plugin jquery.nette.js je univerzální a můžete ho použít v jakékoliv Nette aplikaci, nejen v Automatu na kávu. Samozřejmě ho musíme zalinkovat v šabloně @layout.phtml a doplnit CSS styl pro  ajax-spinner.

<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/jquery.nette.js" type="text/javascript"></script> 

Na straně JavaScriptu máme hotovo, pojďme se podívat na stranu PHP aplikace. Naučit presenter odpovídat na AJAXové požadavky bude velmi jednoduché. Vlastně stačí jediné – označít v šabloně fragmenty HTML, tedy místa, které se mají přenášet. K tomu slouží párová značka {snippet}...{/snippet}. O zbytek už se postará framework.

<body>
        {snippet}
        {include $content}
        {/snippet}
</body> 

Pravda, je tu ještě jedna věc – ve třetí části seriálu jsme si říkali, že po vykonání příkazu by mělo následovat přesměrování na další stránku a následně jsme do metody MachinePresenter::handleInsert() takové přesměrování přidali. V AJAXu ale přesměrovat nechceme, takže volání metody redirect podmíníme otázkou, zda je aktuální požadavek AJAXový. K čemuž slouží metoda  isAjax().

Soubor MachinePresenter.php:

public function handleInsert($coin)
{
        // zvýšíme hodnotu vhozených mincí
        $this->money += max(0, (int) $coin);

        // po příkazu musí následovat přesměrování, ale ne v AJAXu
        if (!$this->isAjax()) {
                $this->redirect('this');
        }
} 

A to je vše! Ano, právě jsme naučili automat na kávu AJAX. AJAXnadno! Přičemž automat je stále plně funkční i pro uživatele bez JavaScriptu.

Zdrojový kód diskutovaný v článku je k dispozici ke stažení.

Celá problematika AJAXu je mnohem zajímavější a rozmanitější, takže v příštím díle se podíváme na HTML fragmenty podrobněji a ukážeme si další formy komunikace.


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.

Používáte AJAX?

David Grudl školí, je autorem PHP knihoven Nette Framework, databázové vrstvy dibi a formátovače HTML kódu Texy!.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 13

Přehled komentářů

yeah RE: Nette Framework: AJAX
David Grudl RE: Nette Framework: AJAX
MazeGen Automat na kávu
rokerkony Re: Automat na kávu
Golf AMF & Flex
David Grudl Re: AMF & Flex
Martin Malý Re: AMF & Flex
David Grudl Re: AMF & Flex
Dundee5 RE: Nette Framework: AJAX
petiar skvelý seriál
trantaloid AJAXnadno!
mival1234 Tutorial pro Nette 1 alfa
David Grudl Re: Tutorial pro Nette 1 alfa
Zdroj: https://www.zdrojak.cz/?p=2995