AJAX & Snippet'ler
Sunucu ve tarayıcı arasında işlevselliğin sıklıkla bölündüğü modern web uygulamaları çağında, AJAX vazgeçilmez bir bağlantı elemanıdır. Nette Framework bize bu alanda hangi seçenekleri sunuyor?
- şablonun parçalarını, yani snippet'leri gönderme
- PHP ve JavaScript arasında değişkenleri iletme
- AJAX isteklerinin hatalarını ayıklama araçları
AJAX İsteği
Bir AJAX isteği, temelde klasik bir HTTP isteğinden farklı değildir. Belirli parametrelerle bir presenter çağrılır. Ve isteğe nasıl yanıt vereceği presenter'a bağlıdır – JSON formatında veri döndürebilir, HTML kodunun bir kısmını, bir XML belgesini vb. gönderebilir.
Tarayıcı tarafında, fetch()
fonksiyonunu kullanarak bir AJAX isteği başlatırız:
fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
})
.then(response => response.json())
.then(payload => {
// yanıtın işlenmesi
});
Sunucu tarafında, HTTP isteğini kapsayan servisin
$httpRequest->isAjax()
metoduyla bir AJAX isteğini tanırız. Algılama için X-Requested-With
HTTP
başlığını kullanır, bu yüzden onu göndermek önemlidir. Presenter içinde $this->isAjax()
metodunu
kullanabilirsiniz.
Verileri JSON formatında göndermek istiyorsanız, sendJson()
metodunu kullanın. Metot
ayrıca presenter'ın etkinliğini de sonlandırır.
public function actionExport(): void
{
$this->sendJson($this->model->getData);
}
AJAX için tasarlanmış özel bir şablonla yanıt vermeyi planlıyorsanız, bunu aşağıdaki gibi yapabilirsiniz:
public function handleClick($param): void
{
if ($this->isAjax()) {
$this->template->setFile('path/to/ajax.latte');
}
// ...
}
Snippet'ler
Nette'nin sunucuyu istemciyle bağlamak için sunduğu en güçlü araç snippet'lerdir. Onlar sayesinde, sıradan bir uygulamayı minimum çaba ve birkaç satır kodla AJAX uygulamasına dönüştürebilirsiniz. Tüm bunların nasıl çalıştığını, kodunu GitHub'da bulabileceğiniz Fifteen örneği göstermektedir.
Snippet'ler veya kesitler, tüm sayfanın yeniden yüklenmesi yerine sayfanın yalnızca bölümlerini güncellemenize olanak tanır. Bu sadece daha hızlı ve daha verimli olmakla kalmaz, aynı zamanda daha rahat bir kullanıcı deneyimi de sağlar. Snippet'ler size Ruby on Rails için Hotwire'ı veya Symfony UX Turbo'yu hatırlatabilir. İlginç bir şekilde, Nette snippet'leri 14 yıl önce tanıttı.
Snippet'ler nasıl çalışır? Sayfa ilk yüklendiğinde (AJAX olmayan istek), tüm snippet'ler dahil olmak üzere tüm sayfa yüklenir. Kullanıcı sayfayla etkileşime girdiğinde (örneğin, bir düğmeye tıkladığında, bir form gönderdiğinde vb.), tüm sayfayı yüklemek yerine bir AJAX isteği tetiklenir. Presenter'daki kod eylemi gerçekleştirir ve hangi snippet'lerin güncellenmesi gerektiğine karar verir. Nette bu snippet'leri oluşturur ve JSON formatında bir dizi olarak gönderir. Tarayıcıdaki işleyici kod, alınan snippet'leri sayfaya geri ekler. Bu nedenle, yalnızca değiştirilen snippet'lerin kodu aktarılır, bu da bant genişliğinden tasarruf sağlar ve tüm sayfanın içeriğini aktarmaya kıyasla yüklemeyi hızlandırır.
Naja
Snippet'leri tarayıcı tarafında işlemek için Naja kütüphanesi kullanılır. Bunu bir node.js paketi olarak kurun (Webpack, Rollup, Vite, Parcel ve diğer uygulamalarla kullanım için):
npm install naja
…veya doğrudan sayfa şablonuna ekleyin:
<script src="https://unpkg.com/naja@2/dist/Naja.min.js"></script>
Önce kütüphaneyi başlatmanız gerekir:
naja.initialize();
Sıradan bir bağlantıdan (sinyal) veya form gönderiminden bir AJAX isteği oluşturmak için, ilgili bağlantıyı, formu
veya düğmeyi ajax
sınıfıyla işaretlemek yeterlidir:
<a n:href="go!" class="ajax">Git</a>
<form n:name="form" class="ajax">
<input n:name="submit">
</form>
veya
<form n:name="form">
<input n:name="submit" class="ajax">
</form>
Snippet'lerin Yeniden Çizilmesi
Control sınıfının her nesnesi (Presenter'ın kendisi dahil),
yeniden çizilmesini gerektiren değişiklikler olup olmadığını takip eder. Bunun için redrawControl()
metodu
kullanılır:
public function handleLogin(string $user): void
{
// giriş yaptıktan sonra ilgili bölümü yeniden çizmek gerekir
$this->redrawControl();
// ...
}
Nette, neyin yeniden çizileceği konusunda daha da hassas kontrol sağlar. Bahsedilen metot, argüman olarak snippet adını alabilir. Böylece, şablonun bölümleri düzeyinde geçersiz kılma (yani yeniden çizmeyi zorlama) mümkündür. Tüm bileşen geçersiz kılınırsa, her bir snippet'i de yeniden çizilir:
// 'header' snippet'ini geçersiz kılar
$this->redrawControl('header');
Latte'de Snippet'ler
Latte'de snippet kullanmak son derece kolaydır. Şablonun bir bölümünü snippet olarak tanımlamak için, onu
{snippet}
ve {/snippet}
etiketleriyle sarmanız yeterlidir:
{snippet header}
<h1>Merhaba ... </h1>
{/snippet}
Snippet, HTML sayfasında özel olarak oluşturulmuş bir id
ile bir <div>
öğesi oluşturur.
Snippet yeniden çizildiğinde, bu öğenin içeriği güncellenir. Bu nedenle, sayfanın ilk oluşturulmasında, başlangıçta
boş olsalar bile tüm snippet'lerin de oluşturulması gerekir.
n:attribute kullanarak <div>
dışında bir öğeyle de bir snippet oluşturabilirsiniz:
<article n:snippet="header" class="foo bar">
<h1>Merhaba ... </h1>
</article>
Snippet Alanları
Snippet adları ifadeler de olabilir:
{foreach $items as $id => $item}
<li n:snippet="item-{$id}">{$item}</li>
{/foreach}
Bu şekilde birkaç snippet oluşturulur: item-0
, item-1
vb. Dinamik bir snippet'i doğrudan
geçersiz kılsaydık (örneğin item-1
), hiçbir şey yeniden çizilmezdi. Bunun nedeni, snippet'lerin gerçekten
kesitler gibi çalışması ve yalnızca kendilerinin doğrudan oluşturulmasıdır. Ancak şablonda aslında item-1
adında bir snippet yoktur. Bu, yalnızca snippet'in etrafındaki kodun, yani foreach döngüsünün yürütülmesiyle ortaya
çıkar. Bu nedenle, yürütülmesi gereken şablon bölümünü {snippetArea}
etiketiyle işaretleriz:
<ul n:snippetArea="itemsContainer">
{foreach $items as $id => $item}
<li n:snippet="item-{$id}">{$item}</li>
{/foreach}
</ul>
Ve hem snippet'in kendisini hem de tüm üst alanı yeniden çizeriz:
$this->redrawControl('itemsContainer');
$this->redrawControl('item-1');
Aynı zamanda, $items
dizisinin yalnızca yeniden çizilmesi gereken öğeleri içermesini sağlamak uygundur.
Şablona {include}
etiketi kullanarak snippet'ler içeren başka bir şablon eklersek, şablon eklemesini tekrar
snippetArea
içine almalı ve onu snippet ile birlikte geçersiz kılmalıyız:
{snippetArea include}
{include 'included.latte'}
{/snippetArea}
{* included.latte *}
{snippet item}
...
{/snippet}
$this->redrawControl('include');
$this->redrawControl('item');
Bileşenlerde Snippet'ler
Bileşenlerde de snippet'ler oluşturabilirsiniz ve Nette
bunları otomatik olarak yeniden çizer. Ancak burada belirli bir sınırlama vardır: snippet'leri yeniden çizmek için
render()
metodunu parametresiz çağırır. Yani, şablonda parametreleri iletmek işe yaramaz:
OK
{control productGrid}
işe yaramayacak:
{control productGrid $arg, $arg}
{control productGrid:paginator}
Kullanıcı Verilerini Gönderme
Snippet'lerle birlikte istemciye herhangi bir ek veri gönderebilirsiniz. Bunları payload
nesnesine yazmanız
yeterlidir:
public function actionDelete(int $id): void
{
// ...
if ($this->isAjax()) {
$this->payload->message = 'Başarılı';
}
}
Parametreleri İletme
Bir bileşene AJAX isteği ile parametreler gönderirsek, bunlar ister sinyal parametreleri ister kalıcı parametreler olsun,
istekte bileşenin adını da içeren global adlarını belirtmemiz gerekir. Parametrenin tam adını
getParameterId()
metodu döndürür.
let url = new URL({link //foo!});
url.searchParams.set({$control->getParameterId('bar')}, bar);
fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
})
Ve bileşendeki karşılık gelen parametrelerle handle metodu:
public function handleFoo(int $bar): void
{
}