AJAX v quickstartu – vynucení aktualizace více tabulek
- Rike
- Člen | 19
Chci jen upozornit na dotaz, který jsem vznesl v místech, kam asi málokdo ze znalců Nette zajde. Jde o příklad na AJAX ve Quickstart (https://doc.nette.org/cs/quickstart#…). Snažím se v něm dosáhnout toho efektu, že signál donutí „aktualizovat“ více tabulek najednou. Předem děkuju za tipy.
- Rike
- Člen | 19
No ta „první aplikace“ u mě dospěla do stádia, že mám a úvodní stránce dvě tabulky – přehled mých úloh a přehled všech nesplněných úloh. U obou tabulek je ajaxem volatelný signál pro označení úlohy za hotovou. Problém je, že nevím, co v presenteru udělat, aby reakce na signál ?userTasks-taskId=1&do=userTasks-markDone ovlivnila i tabulku incompleteTasks a obráceně, reakce na signál ?incompleteTasks-taskId=1&do=incompleteTasks-markDone tabulku userTasks. Jsou to dvě různé komponenty, při dožadování se signálem jedné konkrétní ta druhá vlastně vůbec neexistuje (myslím), takže ani není co invalidovat.
Asi se nevyjadřuju přesně, jsem v Nette začátečník, právě na té „první aplikaci“ se snažím pochopit fungování Nette a posoudit, zda ho budu využívat, a tohle by mi hodně pomohlo.
- David Ďurika
- Člen | 328
ale ano existuje, v tom henlery mozes invalidovat aj viac komponent…
<?php
$this->invalidateControl('table1');
$this->invalidateControl('table2');
?>
- Rike
- Člen | 19
To jsem pochopitelně zkoušel jako pvní, ale table2 potřebuje nějak dostat „nová data“, protože signál volá jen konkrétní komponentu (signalReceiver), avšak table2 renderovala jiná komponenta, která z nějakého důvodu mlčí. Kdyžtak to můžete zkusit na https://github.com/…start/tree/7, konkrétně jde o komponentu https://github.com/…TaskList.php, handleMarkDone(). Ten kód jsem měl dát hned na začátku, snáze se v tom budete orientovat, než když to tu vypisuju „svými slovy“.
- David Ďurika
- Člen | 328
aha ten handle je v komponente… tak to bude musiet mat ten handle v presentery
- Rike
- Člen | 19
Pak ale v šabloně komponenty https://github.com/…skList.latte
neustále hledá odkaz
<a n:if="!$task->done" n:href="markDone! $task->id" class="icon tick ajax">hotovo</a>
metodu komponenty TaskListControl::handleMarkDone(), avšak marně…
Nedokážu ho donutit odkazovat presenter (Homepage:markDone).
Jinak, představoval bych si to tak, že presenter zachytí signál a vyzve dle jeho obsahu komponenty k „vyjádření“, tedy i invalidaci.
Editoval Rike (18. 9. 2012 16:30)
- Rike
- Člen | 19
Eda napsal(a):
Zkoušel jsi:
<a ... href='{plink Homepage:markDone}' ...>hotovo</a>
?
I přes veškerou snahu jsem nedonutil AJAX, aby mi vrátil při handleMarkDone nebo actionMarkDone v HomepagePresenteru potřebnou vizuální změnu. Prostě mlčí, při reloadu stránky je pak patrné, že se akce vykonala.
Zkusme to tedy obecně. Dá se vůbec donutit AJAX signálem do presenteru komponenta k překreslení svého obsahu a vrácení?
Tak jsem nakonec vymyslel toto:
v HomepagePresenteru prostě zavolám komponenty, vynutím si tedy jejich
vytvoření a pak i překreslení. Ale nevím, nakolik je to
„Nette“ :-)
<?php
public function handleMarkDone($taskId) {
$this->tasks->markDone($taskId);
if (!$this->presenter->isAjax()) {
$this->presenter->redirect('this');
} else {
$this->getComponent('incompleteTasks')->invalidateControl();
$this->getComponent('userTasks')->invalidateControl();
}
}
?>
Odkaz s šabloně je pak:
<a n:if="!$task->done" href="{plink markDone! $task->id}" class="icon tick ajax">hotovo</a>
Editoval Rike (19. 9. 2012 9:35)
- Vojtěch Dobeš
- Gold Partner | 1316
Pokud jsem dobře pochopil výchozí zadání, tak máš dvě komponenty, každá vykresluje tabulku, a každá implementuje svůj signál na označení řádku (té úlohy) jako splněné. Snippet předpokládám obaluje celou tabulku. A ty chceš, aby při zavolání signálu jedné komponenty se překreslila i ta druhá.
Lze to uděla jednoduše (ačkoliv to zvyšuje provázání těch komponent…):
public function handleMarkDone($taskId)
{
$this->tasks->markDone($taskId);
if (!$this->presenter->isAjax()) {
$this->presenter->redirect('this');
} else {
$this->invalidateControl();
$this->presenter['nazevDruheKomponenty']->invalidateControl();
}
}
Anebo to můžeš udělat profi :) – využít událostního systému
poskytnutého třídou Nette\Object
(když tak mrkni do dokumentace
do kapitoly „Rozšíření PHP“).
Komponenta:
public $onMarkDone = array();
public function handleMarkDone($taskId)
{
$this->tasks->markDone($taskId);
if ($this->presenter->isAjax()) {
$this->invalidateControl();
}
$this->onMarkDone();
}
Presenter:
protected function createComponentTaskList()
{
if ($this->list === NULL) {
$this->error('Wrong action');
}
$taskList = new Todo\TaskListControl($this->taskLists->tasksOf($this->list));
$taskList->onMarkDone[] = $this->processTaskMarkDone;
return $taskList;
}
public function processTaskMarkDone()
{
if (!$this->isAjax()) {
$this->redirect('this');
} else {
$this['nazevDruheKomponenty']->invalidateControl();
}
}
Tahle implementace s využitím událostí je dost podobná tomu, jak fungují formuláře, až na to, že ty sám ve své komponentě rozhoduješ, jak se budou dostupné událost jmenovat a kdy se zavolají.
Editoval vojtech.dobes (14. 12. 2012 7:16)
- Rike
- Člen | 19
Asi budu volit nějakou čistší verzi Observer/Observable, ale díky za nakopnutí správným směrem. Zrovna ta část, která je uvedená v kapitole „Rozšíření PHP“ mi přijde na Nette dost nešťastná, zejmána pro lidi z jiných jazyků, protože aby se měnily metody v parametry apod., to nemá jind moc obdoby, tak se tomu raději vyhýbám (je to špatný návyk).
Ještě by stálo za zvážení to jednodušší řešení zakomponovat do článku https://doc.nette.org/cs/quickstart, pro budoucí:-)
- Vojtěch Dobeš
- Gold Partner | 1316
Nevím, jestli je to špatný návyk, ale rozhodně je druhé řešení lepší než první, protože v druhém nejsou obě komponenty provázané, a tudíž jsou znovupoužitelné a snáze jednotkově testovatelné.
První variantu bych do Quickstartu určitě neuváděl – jednak by ho zbytečně zesložitila, druhak zdaleka nejde o best-practice, ale spíš naopak!
Editoval vojtech.dobes (19. 9. 2012 10:47)
- salda
- Člen | 1
Jen něco doplním(a zároveň si udělám první post :-) ), kdyby někdo nemohl přijít na to jak to udělat, aby když se klikne na „hotovo“ u mojich úkolů, aby se to projevilo i u těch všech nedokončených a vise versa…
<?php
public function handleMarkDone($taskId)
{
$this->taskRepository->markDone($taskId);
if (!$this->presenter->isAjax())
{
$this->presenter->redirect('this');
}
else {
$this->invalidateControl();
if($this->presenter->name == "incompleteTasks" )
{
$this->presenter["userTasks"]->invalidateControl();
}
else {
$this->presenter["incompleteTasks"]->invalidateControl();
}
}
}
?>
- geb
- Člen | 6
Metoda createComponentTaskList by mela vracet objekt komponenty. To prvni reseni je poloviční, v případě že někdo klikne na MarkDone v druhé komponentě, tak metoda invalidateControl(); zinvaliduje celou druhou komponentu a poté ji ještě jednou zinvaliduje na urovni presenteru tzn komponenta 1 zustane nezinvalidovana.
Editoval geb (13. 12. 2012 19:31)
- Vojtěch Dobeš
- Gold Partner | 1316
@geb Továrničku jsem opravil, díky za upozornění. Co myslíš přesně tím zbytkem tvého příspěvku. Mluvíš tam o těch mých řešeních nebo o něčem jiném?
- Montes
- Člen | 5
Zdravím,
poměrně úspěšně jsem se prokousával QS až po kapitolu
Ajax. Seknul jsem se na výše uvedeném problému, kdy se
neobnovovaly oba dva seznamy úkolů (seznam úkolů přihlášeného uživatele
a seznam úkolů všech uživatelů). Zkusil jsem nejprve metodu
handleMarkDone uživatele salda, ale ta
nepracovala korektně v obou směrech. Tak jsem ji upravil a při označeni
položky jako hotová se obnoví oba dva seznamy a je jedno, v kterém seznamu
to provedu. Chci se zeptat jestli je to přijatelná úprava nebo jestli je to
„prasárna“, popř. jestli to má nějaké úskalí. Díky za odpověď.
<?php
public function handleMarkDone($taskId) {
$this->taskRepository->markDone($taskId);
if (!$this->presenter->isAjax()) {
$this->presenter->redirect('this');
} else {
$this->presenter["userTasks"]->invalidateControl();
$this->presenter["incompleteTasks"]->invalidateControl();
}
}
- kubco2
- Člen | 9
@Montes
Nie je to OK, pretoze to je komponenta, a pri tvojej metode uz neinvaliduje
sama seba, tj. v inej casti aplikacie(v zoznamoch) nefunguje a hadze
Exceptions, pretoze tie vymenovane komponenty na aktualnej stranke
neexistuju.
Musi tam byt
<?php
// zvysok kodu...
else {
$this->invalidateControl(); // aby bola pouzitelna kdekolvek
//nase specialne pripady
//mozno by bolo lepsie nejaky handleMarkDoneCallback davat do kazdej takej komponenty co ma urobit, namiesto natvrdo pomocou if
if ($this->name== "incompleteTasks" ) {
$this->presenter["userTasks"]->invalidateControl();
}
if ($this->name== "userTasks" ) {
$this->presenter["incompleteTasks"]->invalidateControl();
}
}
//zvysok kodu...
?>
Editoval kubco2 (20. 2. 2013 21:51)