AJAX & Snippets
În era aplicațiilor web moderne, în care funcționalitatea se întinde adesea între server și browser, AJAX este un element de legătură esențial. Ce opțiuni oferă Nette Framework în acest domeniu?
- trimiterea unor părți din șablon, așa-numitele snippet-uri
- transmiterea de variabile între PHP și JavaScript
- instrumente de depanare a cererilor AJAX
Cerere AJAX
O cerere AJAX nu diferă în mod fundamental de o cerere HTTP clasică. Un prezentator este apelat cu parametri specifici. Depinde de prezentator cum să răspundă la cerere – acesta poate returna date în format JSON, poate trimite o parte din codul HTML, un document XML etc.
Pe partea de browser, inițiem o cerere AJAX utilizând funcția fetch()
:
fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
})
.then(response => response.json())
.then(payload => {
// prelucrarea răspunsului
});
Pe partea serverului, o cerere AJAX este recunoscută de metoda $httpRequest->isAjax()
a serviciului care încapsulează cererea HTTP. Aceasta utilizează antetul HTTP
X-Requested-With
, deci este esențial să o trimitem. În cadrul prezentatorului, puteți utiliza metoda
$this->isAjax()
.
Dacă doriți să trimiteți date în format JSON, utilizați metoda sendJson()
metoda . Metoda
încheie, de asemenea, activitatea prezentatorului.
public function actionExport(): void
{
$this->sendJson($this->model->getData);
}
Dacă intenționați să răspundeți cu un șablon special conceput pentru AJAX, puteți face acest lucru după cum urmează:
public function handleClick($param): void
{
if ($this->isAjax()) {
$this->template->setFile('path/to/ajax.latte');
}
//...
}
Fragmente
Cel mai puternic instrument oferit de Nette pentru conectarea serverului cu clientul sunt snippets. Cu ajutorul acestora, puteți transforma o aplicație obișnuită într-una AJAX cu un efort minim și câteva linii de cod. Exemplul Fifteen demonstrează cum funcționează totul, iar codul său poate fi găsit pe GitHub.
Snippets, sau clippings, vă permit să actualizați doar părți ale paginii, în loc să reîncărcați întreaga pagină. Acest lucru este mai rapid și mai eficient și oferă, de asemenea, o experiență mai confortabilă pentru utilizator. Snippets s-ar putea să vă amintească de Hotwire pentru Ruby on Rails sau Symfony UX Turbo. Interesant este că Nette a introdus snippets cu 14 ani mai devreme.
Cum funcționează snippets? Atunci când pagina este încărcată pentru prima dată (o cerere non-AJAX), se încarcă întreaga pagină, inclusiv toate snippet-urile. Atunci când utilizatorul interacționează cu pagina (de exemplu, face clic pe un buton, trimite un formular etc.), în loc să se încarce întreaga pagină, se face o cerere AJAX. Codul din prezentator execută acțiunea și decide ce fragmente trebuie actualizate. Nette redă aceste fragmente și le trimite sub forma unei matrice JSON. Codul de manipulare din browser inserează apoi fragmentele primite înapoi în pagină. Prin urmare, este transferat doar codul fragmentelor modificate, ceea ce permite economisirea lățimii de bandă și accelerarea încărcării în comparație cu transferul întregului conținut al paginii.
Naja
Pentru a gestiona fragmente de text în browser, se utilizează biblioteca Naja. Instalați-o ca un pachet node.js (pentru a fi utilizată cu aplicații precum Webpack, Rollup, Vite, Parcel și altele):
npm install naja
… sau inserați-o direct în șablonul de pagină:
<script src="https://unpkg.com/naja@2/dist/Naja.min.js"></script>
Mai întâi trebuie să inițializați biblioteca:
naja.initialize();
Pentru a transforma un link obișnuit (semnal) sau un formular de trimitere într-o cerere AJAX, este suficient să marcați
link-ul, formularul sau butonul respectiv cu clasa 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>
Redesenarea fragmentelor
Fiecare obiect al clasei Control (inclusiv prezentatorul
însuși) păstrează o evidență a modificărilor care au avut loc și care necesită redesenarea sa. Metoda
redrawControl()
este utilizată în acest scop.
public function handleLogin(string $user): void
{
// după logare, este necesar să se redeseneze partea relevantă
$this->redrawControl();
//...
}
Nette permite, de asemenea, un control mai fin al elementelor care trebuie redesenate. Metoda menționată mai sus poate primi ca argument numele fragmentului. Astfel, este posibil să se invalideze (adică să se forțeze o redesenare) la nivelul părții de șablon. În cazul în care întreaga componentă este invalidată, fiecare fragment din aceasta este, de asemenea, redesenat:
// invalidează fragmentul de "antet
$this->redrawControl('header');
Fragmente în Latte
Utilizarea snippet-urilor în Latte este extrem de ușoară. Pentru a defini o parte a șablonului ca fiind un snippet, este
suficient să o înfășurați în etichetele {snippet}
și {/snippet}
:
{snippet header}
<h1>Hello ... </h1>
{/snippet}
Snippet-ul creează un element <div>
în pagina HTML cu o etichetă special generată id
.
Atunci când se redesenează un fragment, conținutul acestui element este actualizat. Prin urmare, atunci când pagina este
redată inițial, toate fragmentele trebuie, de asemenea, redate, chiar dacă acestea pot fi inițial goale.
De asemenea, puteți crea un fragment cu un alt element decât <div>
utilizând un atribut n::
<article n:snippet="header" class="foo bar">
<h1>Hello ... </h1>
</article>
Zone de fragmente
Numele snippet-urilor pot fi, de asemenea, expresii:
{foreach $items as $id => $item}
<li n:snippet="item-{$id}">{$item}</li>
{/foreach}
În acest fel, vom obține mai multe fragmente precum item-0
, item-1
, etc. Dacă ar fi să invalidăm
direct un fragment dinamic (de exemplu, item-1
), nimic nu ar fi redesenat. Motivul este că snippet-urile
funcționează ca adevărate extrase și doar ele însele sunt redate direct. Cu toate acestea, în șablon, nu există, din punct
de vedere tehnic, un fragment numit item-1
. Acesta apare doar atunci când se execută codul care înconjoară
fragmentul, în acest caz, bucla foreach. Prin urmare, vom marca partea din șablon care trebuie executată cu eticheta
{snippetArea}
:
<ul n:snippetArea="itemsContainer">
{foreach $items as $id => $item}
<li n:snippet="item-{$id}">{$item}</li>
{/foreach}
</ul>
Și vom redesena atât fragmentul individual, cât și întreaga zonă de ansamblu:
$this->redrawControl('itemsContainer');
$this->redrawControl('item-1');
De asemenea, este esențial să ne asigurăm că matricea $items
conține doar elementele care trebuie
redesenate.
Atunci când se inserează un alt șablon în șablonul principal folosind eticheta {include}
, care are fragmente,
este necesar să se înfășoare din nou șablonul inclus într-un snippetArea
și să se invalideze atât
fragmentul, cât și zona împreună:
{snippetArea include}
{include 'included.latte'}
{/snippetArea}
{* inclus.latte *}
{snippet item}
...
{/snippet}
$this->redrawControl('include');
$this->redrawControl('item');
Snippets în componente
Puteți crea fragmente în cadrul componentelor, iar Nette le va
redesena automat. Cu toate acestea, există o limitare specifică: pentru a redesena snippets, se apelează metoda
render()
fără niciun parametru. Astfel, trecerea parametrilor în șablon nu va funcționa:
OK
{control productGrid}
will not work:
{control productGrid $arg, $arg}
{control productGrid:paginator}
Trimiterea datelor de utilizator
Împreună cu fragmente, puteți trimite orice date suplimentare către client. Pur și simplu scrieți-le în obiectul
payload
:
public function actionDelete(int $id): void
{
//...
if ($this->isAjax()) {
$this->payload->message = 'Success';
}
}
Parametrii de trimitere
Atunci când trimitem parametrii către componentă prin intermediul unei cereri AJAX, fie că este vorba de parametri de
semnal sau de parametri persistenți, trebuie să furnizăm numele lor global, care conține și numele componentei. Numele
complet al parametrului returnează metoda getParameterId()
.
let url = new URL({link //foo!});
url.searchParams.set({$control->getParameterId('bar')}, bar);
fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
})
O metodă handle cu parametrii corespunzători din componentă:
public function handleFoo(int $bar): void
{
}