AJAX ve Snippet'ler

İşlevselliğin genellikle sunucu ve tarayıcı arasında yayıldığı modern web uygulamaları çağında, AJAX önemli bir bağlantı unsurudur. Nette Framework bu alanda hangi seçenekleri sunuyor?

  • Şablonun snippet olarak adlandırılan parçalarını gönderme
  • PHP ve JavaScript arasında değişken aktarımı
  • AJAX isteklerinde hata ayıklama araçları

AJAX İsteği

Bir AJAX isteği temelde klasik bir HTTP isteğinden farklı değildir. Bir sunucu belirli parametrelerle çağrılır. İsteğe nasıl yanıt verileceği sunucuya bağlıdır – JSON biçiminde veri döndürebilir, HTML kodunun bir bölümünü, bir XML belgesini vb. gönderebilir.

Tarayıcı tarafında, fetch() işlevini kullanarak bir AJAX isteği başlatıyoruz:

fetch(url, {
	headers: {'X-Requested-With': 'XMLHttpRequest'},
})
.then(response => response.json())
.then(payload => {
	// yanıtın işlenmesi
});

Sunucu tarafında, bir AJAX isteği, HTTP isteğini kapsülleyen hizmetin $httpRequest->isAjax() yöntemi tarafından tanınır. Bu yöntem X-Requested-With HTTP başlığını kullanır, bu yüzden bunu göndermek çok önemlidir. Sunucu içinde $this->isAjax() yöntemini kullanabilirsiniz.

Verileri JSON biçiminde göndermek istiyorsanız sendJson() yöntemi. Yöntem ayrıca sunum yapan kişinin 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 şekilde yapabilirsiniz:

public function handleClick($param): void
{
	if ($this->isAjax()) {
		$this->template->setFile('path/to/ajax.latte');
	}
	//...
}

Parçacıklar

Sunucuyu istemciye bağlamak için Nette tarafından sunulan en güçlü araç snippet'lerdir. Bunlarla, sıradan bir uygulamayı minimum çaba ve birkaç satır kodla bir AJAX uygulamasına dönüştürebilirsiniz. Fifteen örneği tüm bunların nasıl çalıştığını göstermektedir ve kodu GitHub'da bulunabilir.

Parçacıklar veya kırpmalar, tüm sayfayı yeniden yüklemek yerine sayfanın yalnızca bazı bölümlerini güncellemenize olanak tanır. Bu daha hızlı ve daha verimlidir ve ayrıca daha rahat bir kullanıcı deneyimi 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ıtmıştı.

Parçacıklar nasıl çalışır? Sayfa ilk yüklendiğinde (AJAX olmayan bir istek), tüm snippet'ler dahil olmak üzere sayfanın tamamı yüklenir. Kullanıcı sayfayla etkileşime girdiğinde (örneğin, bir düğmeye tıkladığında, bir form gönderdiğinde vb.), sayfanın tamamını yüklemek yerine bir AJAX isteği yapılır. Sunucudaki kod eylemi gerçekleştirir ve hangi parçacıkların güncellenmesi gerektiğine karar verir. Nette bu parçacıkları işler ve bir JSON dizisi şeklinde gönderir. Tarayıcıdaki işleme kodu daha sonra alınan parçacıkları sayfaya geri ekler. Bu nedenle, yalnızca değiştirilen parçacıkların kodu aktarılır, bant genişliğinden tasarruf edilir ve tüm sayfa içeriğinin aktarılmasına kıyasla yükleme hızlandırılır.

Naja

Tarayıcı tarafında snippet'leri işlemek için Naja kütüphanesi kullanılır. Bunu bir node.js paketi olarak yükleyin (Webpack, Rollup, Vite, Parcel ve diğerleri gibi uygulamalarla kullanmak için):

npm install naja

… veya doğrudan sayfa şablonuna ekleyin:

<script src="https://unpkg.com/naja@2/dist/Naja.min.js"></script>

Sıradan bir bağlantıyı (sinyal) veya form gönderimini AJAX isteği haline getirmek için ilgili bağlantıyı, formu veya düğmeyi ajax sınıfıyla işaretlemeniz yeterlidir:

<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>

Parçacıkları Yeniden Çizme

Control sınıfının her nesnesi (Presenter'ın kendisi de dahil olmak üzere) yeniden çizilmesini gerektirecek değişikliklerin olup olmadığının kaydını tutar. Bu amaç için redrawControl() yöntemi kullanılır.

public function handleLogin(string $user): void
{
	// giriş yaptıktan sonra ilgili kısmı yeniden çizmek gerekir
	$this->redrawControl();
	//...
}

Nette ayrıca nelerin yeniden çizilmesi gerektiğinin daha iyi kontrol edilmesini sağlar. Yukarıda bahsedilen yöntem, snippet adını bir argüman olarak alabilir. Böylece, şablon parçası düzeyinde geçersiz kılmak (yani yeniden çizmeye zorlamak) mümkündür. Bileşenin tamamı geçersiz kılınırsa, her parçacığı da yeniden çizilir:

// 'header' parçacığını geçersiz kılar
$this->redrawControl('header');

Latte'de Parçacıklar

Latte'de snippet'leri kullanmak son derece kolaydır. Şablonun bir bölümünü snippet olarak tanımlamak için, onu {snippet} ve {/snippet} etiketlerine sarmanız yeterlidir:

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

Kod parçacığı bir öğe oluşturur <div> HTML sayfasında özel olarak oluşturulmuş bir id. Bir snippet yeniden çizilirken, bu öğenin içeriği güncellenir. Bu nedenle, sayfa ilk kez render edildiğinde, başlangıçta boş olsalar bile tüm snippet'ler de render edilmelidir.

dışında bir öğe ile de bir snippet oluşturabilirsiniz <div> bir n:özniteliği kullanarak:

<article n:snippet="header" class="foo bar">
	<h1>Hello ... </h1>
</article>

Snippet Alanları

Snippet adları ifadeler de olabilir:

{foreach $items as $id => $item}
	<li n:snippet="item-{$id}">{$item}</li>
{/foreach}

Bu şekilde, item-0, item-1, vb. gibi birkaç snippet elde edeceğiz. Dinamik bir snippet'i (örneğin, item-1) doğrudan geçersiz kılacak olursak, hiçbir şey yeniden çizilmeyecektir. Bunun nedeni, snippet'lerin gerçek alıntılar olarak işlev görmesi ve yalnızca kendilerinin doğrudan render edilmesidir. Ancak, şablonda teknik olarak item-1 adında bir snippet yoktur. Yalnızca snippet'in çevresindeki kod, bu durumda foreach döngüsü çalıştırıldığında ortaya çıkar. Bu nedenle, şablonun çalıştırılması gereken kısmını {snippetArea} etiketiyle işaretleyeceğiz:

<ul n:snippetArea="itemsContainer">
	{foreach $items as $id => $item}
		<li n:snippet="item-{$id}">{$item}</li>
	{/foreach}
</ul>

Ve hem bireysel parçacığı hem de tüm alanı yeniden çizeceğiz:

$this->redrawControl('itemsContainer');
$this->redrawControl('item-1');

Ayrıca $items dizisinin yalnızca yeniden çizilmesi gereken öğeleri içerdiğinden emin olmak da önemlidir.

Parçacıkları olan {include} etiketini kullanarak ana şablona başka bir şablon eklerken, dahil edilen şablonu tekrar bir snippetArea içine sarmak ve hem parçacığı hem de alanı birlikte geçersiz kılmak gerekir:

{snippetArea include}
	{include 'included.latte'}
{/snippetArea}
{* dahil.latte *}
{snippet item}
	...
{/snippet}
$this->redrawControl('include');
$this->redrawControl('item');

Bileşenlerdeki Snippet'ler

Bileşenler içinde parçacıklar oluşturabilirsiniz ve Nette bunları otomatik olarak yeniden çizer. Bununla birlikte, belirli bir sınırlama vardır: parçacıkları yeniden çizmek için, herhangi bir parametre olmadan render() yöntemini çağırır. Bu nedenle, şablonda parametre geçmek işe yaramayacaktır:

OK
{control productGrid}

will not work:
{control productGrid $arg, $arg}
{control productGrid:paginator}

Kullanıcı Verilerinin Gönderilmesi

Parçacıkların yanı sıra, 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 = 'Success';
	}
}

Parametreleri Gönderme

AJAX isteği aracılığıyla bileşene parametreler gönderdiğimizde, ister sinyal parametreleri ister kalıcı parametreler olsun, bileşenin adını da içeren global adlarını sağlamalıyız. Parametrenin tam adı getParameterId() yöntemini döndürür.

let url = new URL({link //foo!});
url.searchParams.set({$control->getParameterId('bar')}, bar);

fetch(url, {
	headers: {'X-Requested-With': 'XMLHttpRequest'},
})

Bileşende ilgili parametreleri içeren bir tutamaç yöntemi:

public function handleFoo(int $bar): void
{
}
versiyon: 4.0