Zamezení vykonání těla renderXXX při AJAXu

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
joe
Člen | 313
+
0
-

Ahoj,

mám celkem „problém“ při přidávání podpory AJAXu na stránky. Všechno mi sice funguje, ale řešení se mi zdá celkem krkolomné. Špatně se mi to popisuje, snad to pochopíte z příkladu:

Představte si stránku, jejíž načtení bude vyžadovat nějaké náročné dotazy na DB a bude také obsahovat možnost vložení komentáře a dalších věcí na podobném principu – to bych chtěl mít přes AJAX.

Kód by tedy vypadal nějak takto:

public function renderDefault() {
	// .
	// . různé volání metod modelů (mohou obsahovat náročné dotazy)
	// .
	$this->template->comments = Comments::get();
}

Dál taky továrničku na vytvoření formuláře a metodu pro jeho zpracování:

public function addCommentFormSubmitted(AppForm $form) {
	// vložení komentáře je ok
	$this->flashMessage('Odeslano');

	if($this->isAjax()) {
		$this->invalidateCotrol('flashes');
		// může následovat vložení dat do payloadu pro další zpracování
		// $this->payload->...
	} else {
		$this->redirect('this');
	}
}

A teď ten problém je, že i při AJAXově odeslaném formuláři se znovu vykoná vše v metodě renderDefault(), tedy i to, co už znovu načítat NECHCI. Zřejmě asi nemůžu nijak ukončit požadavek třeba pomocí $this->sendPayload(), protože potřebuju invalidovat snippety a do JavaScriptu dostat jejich výstup:

Např. při vložení toho komentáře vrátím ty nejnovější uložené od posledně zobrazeného (ukládám si ID posledního zobrazeného komentáře v hidden poli ve formuláři).

Tím tak v každé takové metodě renderXXX musím ověřovat, jestli se jedná o AJAX požadavek nebo ne, pokud ano, tak jestli je odeslaný ten konkrétní formulář a teprve potom vložit potřebná data do šablony (ty nejnovější komentáře), aby mi přišly do JavaScriptu.

Pokud bych na tu samou stránku přidal ještě například anketu, AJAXové hlasování by bylo přes signál handleXXX, kde bych si musel nastavit nějaký příznak (atribut) třídy, že se má při renderování něco změnit. A v té metodě renderDefault opět ověřovat, jestli se jedná o AJAX požadavek a jestli je příznak nastavený a teprve potom zapsat do šablony, abych měl všechny zápisy $this->template->... v render metodách.

Nevím, ale přijde mi to celkem nepřehledné. Nenapadá vás lepší způsob nebo to jinak nejde? Nešlo by invalidovat snippety, aniž by se provedlo to, co je v render metodách (ale to je asi zase hloupost (?))

Díky za odpovědi

mkoubik
Člen | 728
+
0
-

Vytvoř si metodu getComment() a do ní dej to načítání. Pak jí volej z šablony pomocí {$presenter->getComments()}, do db se bude sahat jenom při vykreslování (invalidaci) toho kusu šablony.

joe
Člen | 313
+
0
-

Díky za reakci, ale tak to přece není řešení. Můj problém není tahat data až při invalidaci, ale zamezení toho ostatního, co právě při AJAXových volání pak zahodím. Tedy to, co se skrývá pod komentářem

// různé volání metod modelů (mohou obsahovat náročné dotazy)

Navíc mít pak načítání přímo v šabloně, to si myslím, že bych to tím akorát zase víc znepřehlednil.

mkoubik
Člen | 728
+
0
-

Pak můžeš použít if (!$this->isAjax()), ale přijde mi čistší data načítat až když jsou potřeba, než je pushovat do šablony.

22
Člen | 1478
+
0
-

když je to takovej problém, tak proč si ty věci nedáš do action fáze?

Mikulas Dite
Člen | 756
+
0
-

Řešení od mkoubik je ideální, protože se tím zrychlí každý požadavek na cache, ne jenom ajaxové requesty.

Otázka je, jestli když Nette vytváří snippet, tak zahazuje co nechce, nebo kreslí jenom něco.

Načítání v šabloně nepřehledné imho není, Object a jeho magický property access umožní psát všechny gettery krátce.

Ondřej Mirtes
Člen | 1536
+
0
-

Mně se to volání SQL ze šablony nelíbí, protože když při tom něco v databázi zhavaruje, uživatel skončí s napůl rozbitým výstupem (uprostřed normálního HTML se začne vypisovat ErrorPresenter).

Tharos
Člen | 1030
+
0
-

@Ondřej Mirtes: Pozor, aby tu nezačala další nekončící debata, kde zachytávat chyby. :) Vždycky jsem měl podobný postoj, jako Ty, ale tento lazy přístup má něco do sebe. Jasnou výhodou je, že web má tak lepší odezvu. Hlavní nevýhodu jsi uvedl, ale vezmi si, že k chybě, při které se řekněme hlavní obsah stránky z databáze vydoluje a o pár mikrosekund později nějaký přehled novinek v bočním sloupci už ne, dojde například při jednom z 5000 requestů. Není pak lákavé mít pro 4999 uživatelů rychlejší aplikaci za cenu toho, že jeden chudák obdrží nedokonalý nevalidní výstup?

joe
Člen | 313
+
0
-

22 napsal(a):

když je to takovej problém, tak proč si ty věci nedáš do action fáze?

Action fáze taky nic neřeší, ne? To už „je jedno“, jestli budu podmínkovat v action nebo render fázi. Přijde mi to stejné. Mně by jen zajímalo, jestli pro to není nějaký lepší způsob.

mkoubik napsal(a):

Vytvoř si metodu…

I když bych takovou metodu měl, pak u všech render fází (pokud v nich něco bude) musim stejně rozlišit, jestli jde o AJAX a nebo ne.

@Mikulas Dite:

K pořádnému promyšlenému cachování jsem se zatím moc nedostal.

Otázka je, jestli když Nette vytváří snippet, tak zahazuje co nechce, nebo kreslí jenom něco.

Celkem by se mi líbilo, kdybych v tom signálu mohl zakončit v případě AJAXu $this->sendPayload() s tím, že dojde i k invalidování snippetů a odeslání na výstup. Nemusel bych se pak starat o to, co mám v další render fázi. Takhle vlastně musím mít podmínky na AJAX jak v signálu, tak v render fázi.

@Ondřej Mirtes & @Tharos:
Rád bych řešil jen to, na co jsem se v úvodu ptal :-) a nechtěl bych to odvracet na téma, kde zachytávat chyby.
Za sebe bych je ale chtěl zachytávat ještě dřív než v šabloně – z důvodu co napsal Ondra, i přesto, že k tomu třeba nikdy nemusí dojít.

Takže asi jiný způsob něž if($this->isAjax()) nebude? Nebo jak podobné věci řešíte?

Mikulas Dite
Člen | 756
+
0
-

Tak obecně to řeší právě cache. Pokud se o výstup žádá poprvé, to zdržení na náročném dotazu tam bude a nic se s tím nadělat nedá.

joe
Člen | 313
+
0
-

To je pravda, to mi v tu chvíli nedošlo jak jsi psal o cachování. Ale zase je hloupost cachovat vše a třeba některé náročné dotazy moc cachovat nemusí jít (musí být aktuální). Mně šlo čistě o to, jak nevykonávat to, co je v render fázích při AJAX požadavcích s invalidováním snippetů (takže to teď asi přes render musí projít, aby se ty snippety odeslaly do JS a jinak než s pomocí podmínek toho asi nedosáhnu).