AJAX v quickstartu – vynucení aktualizace více tabulek

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

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.

David Ďurika
Člen | 328
+
0
-

poloz konkretnejsiu otazku / problem … takto ti tazko poradime

Rike
Člen | 19
+
0
-

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

ale ano existuje, v tom henlery mozes invalidovat aj viac komponent…

<?php
$this->invalidateControl('table1');
$this->invalidateControl('table2');
?>
Rike
Člen | 19
+
0
-

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

aha ten handle je v komponente… tak to bude musiet mat ten handle v presentery

Rike
Člen | 19
+
0
-

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)

Eda
Backer | 220
+
0
-

Zkoušel jsi:

<a ... href='{plink Homepage:markDone}' ...>hotovo</a>

?

Rike
Člen | 19
+
0
-

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

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

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

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)

Rike
Člen | 19
+
0
-

No právě, že by to druhý případ dost zkomplikoval:-) Tak aspoň hodím odkaz na toto vlákno do komentářů, protože pokud to někdo probírá (seznámení s první aplikací) trochu poctivě, zarazí se na stejném problému jako já.

Editoval Rike (19. 9. 2012 11:02)

salda
Člen | 1
+
0
-

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

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

@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
+
0
-

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

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