Nette Documentation Preview

syntax
Dynamic Snippets
****************

Quite often in application development there is a need to perform AJAX operations, for example, in individual rows of a table or list items. As an example, we can choose to list articles, allowing the logged-in user to select a "like/dislike" rating for each of them. The code of the presenter and the corresponding template without AJAX will look something like this (I list the most important snippets, the code assumes the existence of a service for marking up the ratings and getting a collection of articles - the specific implementation is not important for the purposes of this tutorial):

```php
public function handleLike(int $articleId): void
{
	$this->ratingService->saveLike($articleId, $this->user->id);
	$this->redirect('this');
}

public function handleUnlike(int $articleId): void
{
	$this->ratingService->removeLike($articleId, $this->user->id);
	$this->redirect('this');
}
```

Template:

```latte
<article n:foreach="$articles as $article">
	<h2>{$article->title}</h2>
	<div class="content">{$article->content}</div>
	{if !$article->liked}
		<a n:href="like! $article->id" class=ajax>I like it</a>
	{else}
		<a n:href="unlike! $article->id" class=ajax>I don't like it anymore</a>
	{/if}
</article>
```


Ajaxization
===========

Let's now bring AJAX to this simple application. Changing the rating of an article is not important enough to require a HTTP request with redirect, so ideally it should be done with AJAX in the background. We'll use the [handler script from add-ons |https://componette.org/vojtech-dobes/nette.ajax.js/] with the usual convention that AJAX links have the CSS class `ajax`.

However, how to do it specifically? Nette offers 2 ways: the dynamic snippet way and the component way. Both of them have their pros and cons, so we will show them one by one.


The Dynamic Snippets Way
========================

In Latte terminology, a dynamic snippet is a specific use case of the `{snippet}` tag where a variable is used in the snippet name. Such a snippet cannot be found just anywhere in the template - it must be wrapped by a static snippet, i.e. a regular one, or inside a `{snippetArea}`. We could modify our template as follows.


```latte
{snippet articlesContainer}
	<article n:foreach="$articles as $article">
		<h2>{$article->title}</h2>
		<div class="content">{$article->content}</div>
		{snippet article-{$article->id}}
			{if !$article->liked}
				<a n:href="like! $article->id" class=ajax>I like it</a>
			{else}
				<a n:href="unlike! $article->id" class=ajax>I don't like it anymore</a>
			{/if}
		{/snippet}
	</article>
{/snippet}
```

Each article now defines a single snippet, which has an article ID in the title. All these snippets are then wrapped together in a single snippet called `articlesContainer`. If we omit this wrapping snippet, Latte will alert us with an exception.

All that's left to do is to add redrawing to the presenter - just redraw the static wrapper.

```php
public function handleLike(int $articleId): void
{
	$this->ratingService->saveLike($articleId, $this->user->id);
	if ($this->isAjax()) {
		$this->redrawControl('articlesContainer');
		// $this->redrawControl('article-' . $articleId); -- není potřeba
	} else {
		$this->redirect('this');
	}
}
```

Modify the sister method `handleUnlike()` in the same way, and AJAX is up and running!

The solution has one downside, however. If we dig more into how the AJAX request works, we find that although the application looks efficient in appearance (it only returns a single snippet for a given article), it actually renders all the snippets on the server. It has placed the desired snippet in our payload, and discarded the others (thus, quite unnecessarily, it also retrieved them from the database).

To optimize this process, we'll need to take action where we pass the `$articles` collection to the template (say in the `renderDefault()` method). We will take advantage of the fact that signal processing takes place before the `render<Something>` methods:

```php
public function handleLike(int $articleId): void
{
	// ...
	if ($this->isAjax()) {
		// ...
		$this->template->articles = [
			$this->connection->table('articles')->get($articleId),
		];
	} else {
		// ...
}

public function renderDefault(): void
{
	if (!isset($this->template->articles)) {
		$this->template->articles = $this->connection->table('articles');
	}
}
```

Now, when the signal is processed, instead of a collection with all articles, only an array with a single article is passed to the template - the one we want to render and send in payload to the browser. Thus, `{foreach}` will be done only once and no extra snippets will be rendered.


Component Way
=============

A completely different solution uses a different approach to avoid dynamic snippets. The trick is to move all the logic into a separate component - from now on, we don't have a presenter to take care of entering the rating, but a dedicated `LikeControl`. The class will look like the following (in addition, it will also contain the `render`, `handleUnlike`, etc. methods):

```php
class LikeControl extends Nette\Application\UI\Control
{
	public function __construct(
		private Article $article,
	) {
	}

	public function handleLike(): void
	{
		$this->ratingService->saveLike($this->article->id, $this->presenter->user->id);
		if ($this->presenter->isAjax()) {
			$this->redrawControl();
		} else {
			$this->presenter->redirect('this');
		}
	}
}
```

Template of component:

```latte
{snippet}
	{if !$article->liked}
		<a n:href="like!" class=ajax>I like it</a>
	{else}
		<a n:href="unlike!" class=ajax>I don't like it anymore</a>
	{/if}
{/snippet}
```

Of course we will change the view template and we will have to add a factory to the presenter. Since we will create the component as many times as we receive articles from the database, we will use the [application:Multiplier] class to "multiply" it.

```php
protected function createComponentLikeControl()
{
	$articles = $this->connection->table('articles');
	return new Nette\Application\UI\Multiplier(function (int $articleId) use ($articles) {
		return new LikeControl($articles[$articleId]);
	});
}
```

The template view is reduced to the minimum necessary (and completely free of snippets!):

```latte
<article n:foreach="$articles as $article">
	<h2>{$article->title}</h2>
	<div class="content">{$article->content}</div>
	{control "likeControl-$article->id"}
</article>
```

We are almost done: the application will now work in AJAX. Here too we have to optimize the application, because due to the use of Nette Database, the signal processing will unnecessarily load all articles from the database instead of one. However, the advantage is that there will be no rendering, because only our component is actually rendered.

{{priority: -1}}
{{sitename: Best Practices}}

Dynamic Snippets

Quite often in application development there is a need to perform AJAX operations, for example, in individual rows of a table or list items. As an example, we can choose to list articles, allowing the logged-in user to select a „like/dislike“ rating for each of them. The code of the presenter and the corresponding template without AJAX will look something like this (I list the most important snippets, the code assumes the existence of a service for marking up the ratings and getting a collection of articles – the specific implementation is not important for the purposes of this tutorial):

public function handleLike(int $articleId): void
{
	$this->ratingService->saveLike($articleId, $this->user->id);
	$this->redirect('this');
}

public function handleUnlike(int $articleId): void
{
	$this->ratingService->removeLike($articleId, $this->user->id);
	$this->redirect('this');
}

Template:

<article n:foreach="$articles as $article">
	<h2>{$article->title}</h2>
	<div class="content">{$article->content}</div>
	{if !$article->liked}
		<a n:href="like! $article->id" class=ajax>I like it</a>
	{else}
		<a n:href="unlike! $article->id" class=ajax>I don't like it anymore</a>
	{/if}
</article>

Ajaxization

Let's now bring AJAX to this simple application. Changing the rating of an article is not important enough to require a HTTP request with redirect, so ideally it should be done with AJAX in the background. We'll use the handler script from add-ons with the usual convention that AJAX links have the CSS class ajax.

However, how to do it specifically? Nette offers 2 ways: the dynamic snippet way and the component way. Both of them have their pros and cons, so we will show them one by one.

The Dynamic Snippets Way

In Latte terminology, a dynamic snippet is a specific use case of the {snippet} tag where a variable is used in the snippet name. Such a snippet cannot be found just anywhere in the template – it must be wrapped by a static snippet, i.e. a regular one, or inside a {snippetArea}. We could modify our template as follows.

{snippet articlesContainer}
	<article n:foreach="$articles as $article">
		<h2>{$article->title}</h2>
		<div class="content">{$article->content}</div>
		{snippet article-{$article->id}}
			{if !$article->liked}
				<a n:href="like! $article->id" class=ajax>I like it</a>
			{else}
				<a n:href="unlike! $article->id" class=ajax>I don't like it anymore</a>
			{/if}
		{/snippet}
	</article>
{/snippet}

Each article now defines a single snippet, which has an article ID in the title. All these snippets are then wrapped together in a single snippet called articlesContainer. If we omit this wrapping snippet, Latte will alert us with an exception.

All that's left to do is to add redrawing to the presenter – just redraw the static wrapper.

public function handleLike(int $articleId): void
{
	$this->ratingService->saveLike($articleId, $this->user->id);
	if ($this->isAjax()) {
		$this->redrawControl('articlesContainer');
		// $this->redrawControl('article-' . $articleId); -- není potřeba
	} else {
		$this->redirect('this');
	}
}

Modify the sister method handleUnlike() in the same way, and AJAX is up and running!

The solution has one downside, however. If we dig more into how the AJAX request works, we find that although the application looks efficient in appearance (it only returns a single snippet for a given article), it actually renders all the snippets on the server. It has placed the desired snippet in our payload, and discarded the others (thus, quite unnecessarily, it also retrieved them from the database).

To optimize this process, we'll need to take action where we pass the $articles collection to the template (say in the renderDefault() method). We will take advantage of the fact that signal processing takes place before the render<Something> methods:

public function handleLike(int $articleId): void
{
	// ...
	if ($this->isAjax()) {
		// ...
		$this->template->articles = [
			$this->connection->table('articles')->get($articleId),
		];
	} else {
		// ...
}

public function renderDefault(): void
{
	if (!isset($this->template->articles)) {
		$this->template->articles = $this->connection->table('articles');
	}
}

Now, when the signal is processed, instead of a collection with all articles, only an array with a single article is passed to the template – the one we want to render and send in payload to the browser. Thus, {foreach} will be done only once and no extra snippets will be rendered.

Component Way

A completely different solution uses a different approach to avoid dynamic snippets. The trick is to move all the logic into a separate component – from now on, we don't have a presenter to take care of entering the rating, but a dedicated LikeControl. The class will look like the following (in addition, it will also contain the render, handleUnlike, etc. methods):

class LikeControl extends Nette\Application\UI\Control
{
	public function __construct(
		private Article $article,
	) {
	}

	public function handleLike(): void
	{
		$this->ratingService->saveLike($this->article->id, $this->presenter->user->id);
		if ($this->presenter->isAjax()) {
			$this->redrawControl();
		} else {
			$this->presenter->redirect('this');
		}
	}
}

Template of component:

{snippet}
	{if !$article->liked}
		<a n:href="like!" class=ajax>I like it</a>
	{else}
		<a n:href="unlike!" class=ajax>I don't like it anymore</a>
	{/if}
{/snippet}

Of course we will change the view template and we will have to add a factory to the presenter. Since we will create the component as many times as we receive articles from the database, we will use the Multiplier class to „multiply“ it.

protected function createComponentLikeControl()
{
	$articles = $this->connection->table('articles');
	return new Nette\Application\UI\Multiplier(function (int $articleId) use ($articles) {
		return new LikeControl($articles[$articleId]);
	});
}

The template view is reduced to the minimum necessary (and completely free of snippets!):

<article n:foreach="$articles as $article">
	<h2>{$article->title}</h2>
	<div class="content">{$article->content}</div>
	{control "likeControl-$article->id"}
</article>

We are almost done: the application will now work in AJAX. Here too we have to optimize the application, because due to the use of Nette Database, the signal processing will unnecessarily load all articles from the database instead of one. However, the advantage is that there will be no rendering, because only our component is actually rendered.