Sortable a AJAXový požadavek

BaruCepa
Člen | 33
+
0
-

Zdravím znovu, do administrace svého projektu jsem zakomponovala funkci jQuery UI Sortable, ta mi funguje krásně. Ale nejsem schopná k tomu napasovat AJAXový požadavek, který by změnu pořadí poslal zpět, abych ji mohla pak reflektovat v databázi a tam uložit nové pořadí. Prošla jsem zde starší dotazy podobného typu, ale nejsem z nich úplně moudrá. Prosím o radu, jak si s tím poradit. Na projektu používám i Docker.

Soubor admin.js:

$("#sortable-list").sortable({
	update: () => {
		const sortedIDs = $( "#sortable-list" ).sortable( "toArray" );

		$.ajax({
			url: "/Administration/changeOrder",
			data: {
				"sortedIDs" : sortedIDs,
			}
		});
	}
});

Handler v presenteru:

public function handleChangeOrder(array $sortedIDs)
	{
		if ($this->isAjax())
		{
			$this->sendJson(['result' => $sortedIDs]);
		}
		else
		{
			$this->error('Toto není AJAXový požadavek.');
		}
	}

Nyní je to ve fázi, kdy sortable funguje krásně, ale dle DevTools nedojde k použití handleru. Při přesunu položky debugovací lišta zobrazí AJAX, ale se špatným presenterem i šablonou. Nový seznam se nejspíš vrací i jako GET, takže je varianta to nějakým způsobem vypreparovat z toho?

Infanticide0
Člen | 64
+
0
-

url: „/Administration/changeOrder“ neni link na signál a natvrdo v šabloně bych ho radil nemít (změníš název handle funkce a přestane to fungovat), vytvoř si link přes presenter ($this->link(„change!“)) a předej jako template proměnnou do šablony, tam ji vypiš v data-url=„{$x}“, to pak čti javascriptem a použij v ajax requestu.

bez zbytečnýho zanořování kódu:

public function handleChangeOrder(array $sortedIDs)
	{
		if (!$this->isAjax())
			$this->error('Toto není AJAXový požadavek.');

		$this->sendJson(['result' => $sortedIDs]);
	}

Editoval Infanticide0 (16. 12. 2023 19:24)

BaruCepa
Člen | 33
+
0
-

@Infanticide0
Dobře, upravila jsem dle tvých připomínek takto:

$("#sortable-list").sortable({
	update: () => {
		const sortedIDs = $( "#sortable-list" ).sortable( "toArray" );

		$.ajax({
			url: "{$url}",
			data: {
				"sortedIDs" : sortedIDs,
			}
		});
	}
});

A presenter :

public function renderDefault()
	{
		if (!$this->getUser()->isLoggedIn())
		{
			$this->redirect('Administration:login');
		}
		else
		{
			$this->template->username = $this->user->identity->username;
			$pages = $this->pageManager->getAllPages();
			$this->template->pages = $pages;

			$url = $this->link("change!");
			$this->template->url = $url;
		}
	}

	public function handleChange(array $sortedIDs)
	{
		if (!$this->isAjax())
		{
			$this->error('Toto není AJAXový požadavek.');
		}

		$this->sendJson(['result' => $sortedIDs]);
	}

Ale teď nevím, co dál. Kde vybrat změněné pořadí, abych s ním mohla dál pracovat? S AJAXem si hraju poprvé a úplně ho ještě nechápu.

Infanticide0
Člen | 64
+
0
-

Koukni na kód, lepší než to složitě vysvětlovat. Proměnné v Latte nikdy neobaluj do uvozovek, o to se postará Latte samo (mělo by ti vyhodit chybu, když to uděláš).

V příkladu dole by se určitě hodilo ještě validovat data z POSTu, možná by to celý šlo předělat do formuláře, aby to validovalo automaticky.

Presenter:

final class HomePresenter extends Nette\Application\UI\Presenter
{
    public function renderDefault(): void
    {
        $this->template->url = $this->link("changeOrder!");
    }

    public function handleChangeOrder(): void
    {
        if(!$this->isAjax())
            $this->error("ajax!");

        $newOrder = $this->request->getPost("items");
        $this->sendJson(['result' => $newOrder]);
    }
}

Template:

<ul id="sortable">
  <li data-value="1" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 1</li>
  <li data-value="2" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 2</li>
  <li data-value="3" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 3</li>
  <li data-value="4" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 4</li>
  <li data-value="5" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 5</li>
  <li data-value="6" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 6</li>
  <li data-value="7" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 7</li>
</ul>

<script src="{$basePath}/jquery.js"></script>
<script src="{$basePath}/jquery-ui.min.js"></script>
<link rel="stylesheet" href="{$basePath}/jquery-ui.min.css">
<script>
$(function() {
	$("#sortable").sortable({
		update: (event, ui) => {
			let sortedIDs = $("#sortable").sortable("toArray", {
				"attribute": "data-value"
			});

			$.ajax({
				type: "POST",
				url: {$url},
				data: {
					"items" : sortedIDs,
				}
			}).done(function(response) {
				console.log(response.result);
			});
		}
	});
});
</script>
BaruCepa
Člen | 33
+
0
-

@Infanticide0
Fuuu, tak jsem prošla, upravila, dosáhla jsem toho, ža už se na Tracy liště zobrazuje AJAX se správným presenterem (routování), ale pořád nedojde na handle metodu, vůbec se to k ní nedostane a tím pádem ani do databáze.
Presenter:

public function renderDefault()
	{
		if (!$this->getUser()->isLoggedIn())
		{
			$this->redirect('Administration:login');
		}
		else
		{
			$this->template->username = $this->user->identity->username;
			$pages = $this->pageManager->getAllPages();
			$this->template->pages = $pages;

			$this->template->url = $this->link("change!");
		}
	}

	public function handleChange()
	{
		if (!$this->isAjax())
		{
			$this->error('Toto není AJAXový požadavek.');
		}

		$newOrder = $this->request->getPost("items");
		$this->pageManager->updatePageOrder($newOrder);

		$this->sendJson(['result' => $newOrder]);
	}

admin.js :

$("#sortable-list").sortable({
	update: () => {
		const sortedIDs = $( "#sortable-list" ).sortable( "toArray" );

		$.ajax({
			url: '{$url}',
			type: "POST",
			data: {
				"items" : sortedIDs,
			}
		}).done(function(response) {
			console.log(response.result);
		});
	}
});

A přidám i RouterFactory, jestli není pes zakopanej tam :

final class RouterFactory
{
	use Nette\StaticClass;

	public static function createRouter(): RouteList
	{
		$router = new RouteList;

		$router->addRoute('administrace/<action>[/<url>]', 'Admin:Administration:default');

		$router->addRoute('<action>[/<url>]', [
			'presenter' => 'Admin:Administration',
			'action' => [
				Route::FILTER_STRICT => true,
				Route::FILTER_TABLE => [
					'administrace' => 'default',
					'prihlaseni' => 'login',
					'odhlasit' => 'logout',
				]
			]
		]);

		$router->addRoute('<action>[/<url>]', [
			'presenter' => 'Admin:Editor',
			'action' => [
				Route::FILTER_STRICT => true,
				Route::FILTER_TABLE => [
					'editace' => 'editor',
					'ulozeni' => 'save',
					'smazani' => 'remove',
				]
			]
		]);

		$router->addRoute('<id .*>', [
			'presenter' => 'Front:Page',
			'action' => 'page',
			'id' => 'uvod',
		]);

		return $router;
	}
}

Jinak jak je v admin.js ten console.log, v konzoli je akorát undefined. Jinak nic, žádné chyby, network se tváří taky dobře, všechno 200.

Infanticide0
Člen | 64
+
+3
-

@BaruCepa

Soubor admin.js ti latte nezpracuje, takže nedosadí tu proměnnou $url, volá ti to nesmyslnou url?
Nahoře jsem psal o předávání url v templatu stránky, že si ji přečteš až javascriptem.

Takhle

template:

<ul id="sortable-list" data-url={$url}>
  ...

admin.js

<script>
$("#sortable-list").sortable({
	update: () => {
		const sortedIDs = $( "#sortable-list" ).sortable( "toArray" );

		$.ajax({
			url: $("#sortable-list").data("url"), // načte data-url z #sortable-list
			...

</script>

Editoval Infanticide0 (19. 12. 2023 15:13)

BaruCepa
Člen | 33
+
0
-

Super, děkuji moc za pomoc a vysvětlení, už to šlape :)