AJAX in sličice
V dobi sodobnih spletnih aplikacij, kjer je funkcionalnost pogosto razpeta med strežnikom in brskalnikom, je AJAX bistven povezovalni element. Katere možnosti ponuja ogrodje Nette na tem področju?
- pošiljanje delov predloge, tako imenovanih snippets
- posredovanje spremenljivk med PHP in JavaScriptom
- orodja za razhroščevanje zahtevkov AJAX
Zahteva AJAX
Zahteva AJAX se v osnovi ne razlikuje od klasične zahteve HTTP. Predlagatelj se pokliče z določenimi parametri. Od predstavnika je odvisno, kako se bo odzval na zahtevo – lahko vrne podatke v obliki JSON, pošlje del kode HTML, dokument XML itd.
Na strani brskalnika sprožimo zahtevo AJAX s funkcijo fetch()
:
fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
})
.then(response => response.json())
.then(payload => {
// obdelava odziva
});
Na strani strežnika zahtevo AJAX prepozna metoda $httpRequest->isAjax()
storitve, ki enkapsulira zahtevo HTTP. Uporablja glavo HTTP X-Requested-With
,
zato jo je nujno poslati. V predstavitvenem programu lahko uporabite metodo $this->isAjax()
.
Če želite poslati podatke v obliki JSON, uporabite metodo sendJson()
metodo. Metoda tudi
zaključi dejavnost predstavitvenega programa.
public function actionExport(): void
{
$this->sendJson($this->model->getData);
}
Če se nameravate odzvati s posebno predlogo, zasnovano za AJAX, lahko to storite na naslednji način:
public function handleClick($param): void
{
if ($this->isAjax()) {
$this->template->setFile('path/to/ajax.latte');
}
//...
}
Utrinki
Najzmogljivejše orodje, ki ga ponuja Nette za povezovanje strežnika z odjemalcem, so snippets. Z njimi lahko navadno aplikacijo spremenite v aplikacijo AJAX z minimalnim naporom in nekaj vrsticami kode. Primer Fifteen prikazuje, kako vse to deluje, njegovo kodo pa lahko najdete na GitHubu.
Snippets ali clippings omogočajo posodabljanje samo delov strani, namesto da bi ponovno naložili celotno stran. To je hitrejše in učinkovitejše, poleg tega pa zagotavlja udobnejšo uporabniško izkušnjo. Snippets vas morda spominjajo na Hotwire za Ruby on Rails ali Symfony UX Turbo. Zanimivo pa je, da je Nette snippets predstavil že 14 let prej.
Kako delujejo odlomki? Ob prvem nalaganju strani (zahteva, ki ni zahteva AJAX) se naloži celotna stran, vključno z vsemi snippets. Ko uporabnik komunicira s stranjo (npr. klikne gumb, odda obrazec itd.), se namesto nalaganja celotne strani izvede zahteva AJAX. Koda v predstavniku izvede dejanje in odloči, katere utrinke je treba posodobiti. Nette te utrinke prikaže in jih pošlje v obliki polja JSON. Obdelovalna koda v brskalniku nato vstavi prejete utrinke nazaj na stran. Zato se prenese samo koda spremenjenih izsekov, kar prihrani pasovno širino in pospeši nalaganje v primerjavi s prenosom celotne vsebine strani.
Naja
Za obdelavo izsekov na strani brskalnika se uporablja knjižnica Naja. Namestite jo kot paket node.js (za uporabo z aplikacijami, kot so Webpack, Rollup, Vite, Parcel in druge):
npm install naja
… ali jo vstavite neposredno v predlogo strani:
<script src="https://unpkg.com/naja@2/dist/Naja.min.js"></script>
Najprej morate knjižnico inicializirati:
naja.initialize();
Če želite, da običajna povezava (signal) ali oddaja obrazca postane zahteva AJAX, preprosto označite ustrezno povezavo,
obrazec ali gumb z razredom ajax
:
<a n:href="go!" class="ajax">Go</a>
<form n:name="form" class="ajax">
<input n:name="submit">
</form>
or
<form n:name="form">
<input n:name="submit" class="ajax">
</form>
Ponovno risanje utrinkov
Vsak objekt razreda Control (vključno s samim Presenterjem)
beleži, ali so se zgodile spremembe, ki zahtevajo njegovo ponovno izrisovanje. V ta namen se uporablja metoda
redrawControl()
.
public function handleLogin(string $user): void
{
// po prijavi je treba ponovno narisati ustrezni del
$this->redrawControl();
//...
}
Nette omogoča tudi natančnejši nadzor nad tem, kaj je treba ponovno narisati. Zgoraj omenjena metoda lahko kot argument sprejme ime izseka. Tako je mogoče razveljaviti (kar pomeni: prisiliti k ponovnemu izrisu) na ravni dela predloge. Če je celotna komponenta razveljavljena, se na novo nariše tudi vsak njen delček:
// razveljavi odlomek 'header'
$this->redrawControl('header');
Utrinki v Latte
Uporaba snippetov v Latte je zelo preprosta. Če želite del predloge opredeliti kot izsek, ga preprosto ovijte v oznake
{snippet}
in {/snippet}
:
{snippet header}
<h1>Hello ... </h1>
{/snippet}
Snippet ustvari element <div>
na strani HTML s posebej ustvarjeno oznako id
. Pri ponovnem
izrisu sličice se vsebina tega elementa posodobi. Zato je treba ob začetnem izrisu strani izrisati tudi vse sličice, čeprav so
lahko na začetku prazne.
Fragment lahko ustvarite tudi z elementom, ki ni <div>
z uporabo atributa n::
<article n:snippet="header" class="foo bar">
<h1>Hello ... </h1>
</article>
Območja sličic
Imena snippetov so lahko tudi izrazi:
{foreach $items as $id => $item}
<li n:snippet="item-{$id}">{$item}</li>
{/foreach}
Tako bomo dobili več izsekov, kot so item-0
, item-1
, itd. Če bi neposredno razveljavili dinamični
snippet (npr. item-1
), se ne bi nič na novo izrisalo. Razlog za to je, da utrinki delujejo kot pravi izvlečki in se
neposredno izrisujejo le oni sami. Vendar v predlogi tehnično ni nobenega odlomka z imenom item-1
. Pojavi se šele
ob izvajanju okoliške kode snippeta, v tem primeru zanke foreach. Zato bomo del predloge, ki ga je treba izvesti, označili
z oznako {snippetArea}
:
<ul n:snippetArea="itemsContainer">
{foreach $items as $id => $item}
<li n:snippet="item-{$id}">{$item}</li>
{/foreach}
</ul>
In na novo bomo narisali tako posamezni izsek kot celotno krovno območje:
$this->redrawControl('itemsContainer');
$this->redrawControl('item-1');
Prav tako je treba zagotoviti, da polje $items
vsebuje samo elemente, ki jih je treba ponovno narisati.
Pri vstavljanju druge predloge v glavno s pomočjo oznake {include}
, ki ima izseke, je treba vključeno predlogo
ponovno zaviti v snippetArea
in razveljaviti tako izsek kot območje skupaj:
{snippetArea include}
{include 'included.latte'}
{/snippetArea}
{* included.latte *}
{snippet item}
...
{/snippet}
$this->redrawControl('include');
$this->redrawControl('item');
Utrinki v sestavinah
V komponentah lahko ustvarite sličice in Nette jih bo samodejno
ponovno narisal. Vendar pa obstaja posebna omejitev: za ponovno izrisovanje snippetov je treba poklicati metodo
render()
brez parametrov. Tako posredovanje parametrov v predlogi ne bo delovalo:
OK
{control productGrid}
will not work:
{control productGrid $arg, $arg}
{control productGrid:paginator}
Pošiljanje uporabniških podatkov
Skupaj z odlomki lahko odjemalcu pošljete vse dodatne podatke. Preprosto jih zapišite v objekt payload
:
public function actionDelete(int $id): void
{
//...
if ($this->isAjax()) {
$this->payload->message = 'Success';
}
}
Pošiljanje parametrov
Ko komponenti z zahtevo AJAX pošiljamo parametre, bodisi signalne bodisi trajne, moramo navesti njihovo globalno ime, ki
vsebuje tudi ime komponente. Polno ime parametra vrne metoda getParameterId()
.
let url = new URL({link //foo!});
url.searchParams.set({$control->getParameterId('bar')}, bar);
fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
})
Metoda handle z ustreznimi parametri v komponenti:
public function handleFoo(int $bar): void
{
}