EN | CS | Přihlásit | Registrovat

Ajax & snippety

AJAXový požadavek lze detekovat metodou třídy (resp. služby) zapouzdřující HTTP požadavek: Environment::getHttpRequest()->isAjax() (detekuje podle HTTP hlavičky X-Requested-With). Uvnitř presenteru je k dispozici „zkratka“ v podobě metody $presenter->isAjax().

AJAXový požadavek se nijak neliší od klasického požadavku – je zavolán presenter s určitým view a parametry. Je také věcí presenteru, jak bude na něj reagovat: může použít vlastní rutinu, která vrátí nějaký fragment HTML kódu (HTML snippet), XML dokument, JSON objekt nebo kód v JavaScriptu. K tomu lze využít i předpřipravený objekt šablony – například tak, že při detekci AJAXu zvolí speciální šablonu:

public function handleClick($param)
{
if ($this->isAjax()) {
$this->template->setFile('...ajax.phtml');
}
...
}

Nicméně daleko silnější nástroj představuje vestavěná podpora AJAXu. Díky ní lze udělat z obyčejné aplikace AJAXovou prakticky několika řádky kódu.

Myšlenka vychází z toho, že při prvotním (tedy neAJAXovém) požadavku se přenese celá stránka a poté se při každém již AJAXovém subrequestu (= požadavku na stejný presenter a view) přenáší pouze kód změněných částí. K tomu slouží dva mechanismy: invalidace a renderování snippetů.

Invalidace

Každý objekt třídy Control (což je i samotný Presenter) si umí zapamatovat, jestli při subrequestu došlo ke změnám, které si vyžadují jej překreslit. K tomu slouží triptych metod invalidateControl(), validateControl() a isControlInvalid(). Příklad:

public function handleLogin($user)
{
// po přihlášení uživatele se musí objekt překreslit
$this->invalidateControl();
...
}

Nette však nabízí ještě jemnější rozlišení, než na úrovni komponent. Uvedené metody mohou totiž jako parametr nabývat název tzv. „snippetu“, nebo-li ústřižku. Lze tedy invalidovat/va­lidovat na úrovni těchto snippetů (každý objekt může mít libovolné množství snippetů). Pokud se invaliduje celá komponenta, tak je i každý snippet považován za invalidní. Komponenta je invalidní i tehdy, pokud je invalidní některá její subkomponenta.

echo $this->isControlInvalid(); // -> FALSE

$this->invalidateControl('header'); // invaliduje snippet 'header'
echo $this->isControlInvalid('header'); // -> TRUE
echo $this->isControlInvalid('footer'); // -> FALSE
echo $this->isControlInvalid(); // -> TRUE, alespoň jeden snippet je invalid

$this->invalidateControl(); // invaliduje celou komponentu, každý snippet
echo $this->isControlInvalid('footer'); // -> TRUE

Komponenta, která přijímá signál, je automaticky označena za invalidní.

Díky invalidaci snippetů přesně víme, které části kterých prvků bude potřeba překreslit.

Renderování snippetů

Nette je založeno na myšlence logických, nikoliv grafických prvků, tj. objekt třídy Control nepředstavuje pravoúhlou oblast ve stránce, ale logickou komponentu, která se může renderovat i do více podob (např. DataGrid může mít jednu metodu pro vykreslení mřížky a druhou pro vykreslení „stránkovadla“ apod). Každý prvek může být navíc na stránce vykreslen vícekrát, nebo podmíněně, nebo pokaždé s jinou šablonou atd.

Není tedy možné jednoduše zavolat metodu render na každém invalidním objektu, vlastně ani žádný interface metodu render nedefinuje. K vykreslování je nutné přistupovat tak, jako když se kreslí celá stránka. (Přesněji řečeno, je možné jít touto cestou a vykreslit pouze invalidní snippety invalidních objektů, ale vaše aplikace musí být k tomu účelu vhodně navržena, kdežto vestavěná podpora AJAXu musí fungovat obecně).

Vykreslování stránky probíhá velmi podobně, jako u neAJAXového požadavku, načtou se tytéž šablony atd. Klíčovým úkolem však je vypustit ty části, které se na výstup vůbec dostat nemají, a ty, které se vykreslit mají, přidružit s identifikátorem a poslat klientovi ve formátu, kterému bude obslužný JavaScript rozumět.

Pokud se uvnitř šablony nachází control nebo snippet, musíme jej obalit párovou značkou {snippet} ... {/snippet} – ty totiž zajistí, že se vykreslený snippet vystřihne a předá AJAXovému ovladači. Také jej obalí pomocnou značkou <div> (lze použít i jinou značku). V uvedeném příkladě je snippet pojmenován jako header a může představovat i například šablonu controlu:

{snippet header}
<h1>Hello .... </h1>
{/snippet}

Při využití této techniky je nutné si dát pozor na vkládané šablony pomocí {include $template}, které obsahují nějaké snippety, aby nebyly přeskočeny. Tomu lze zabránit přidáním zavináče před složené závorky, tedy použitím @{include $template}, který nechá na snippetu rozhodnutí, zda-li se má překreslit (rozhodne se podle proměnné SnippetHelper::$outputAllowed) a vkládanou šablonu správně obalí podmínkami. Šablona, do které vkládáme jinou šablonu obsahující snippet by vypadala například takto:

<html>
<body>
@{include $template}
</body>
</html>

Více informací k zavináčové magii.

Komunikace s prohlížečem

Jednotlivé snippets společně s dalšími (i uživatelskými daty) se předají do úložište, které je dostupné přes $presenter->payload. Presenter předává data ve formátu JSON.

Jak to celé funguje pohromadě demostruje příklad Fifteen, jehož kód najdete v distribuci.


Login to submit a comment