Callback do jiné komponenty

m.brecher
Generous Backer | 736
+
0
-

Ahoj,

zkusil jsem ve formuláři nasměrovat callback onClick[] formuláře do jiné komponenty presenteru a nedaří se to:

Presenter:

final class ProjectPresenter extends AdminPresenter
{

    public function createComponentEditImageForm(): Form
    {
        $form = new Form;
		.......

		$form->addSubmit('deleteButton', 'Smazat vše')
        	->onClick[] = [$this['dialog'], 'handleOpen'];

        return $form;
	}
}

Komponenta dialog v presenteru existuje a volaný handler také, nicméně to vyhazuje chybu:

ReflectionException #-1
Class "string" does not exist

Když volám handler komponenty mezikrokem přes handler presenteru, tak to funguje:

Presenter:

final class ProjectPresenter extends AdminPresenter
{

    public function createComponentEditImageForm(): Form
    {
        $form = new Form;
		.......

		$form->addSubmit('deleteButton', 'Smazat vše')
        	->onClick[] = [$this, 'handleDialogOpen'];

        return $form;
	}

    public function handleDialogOpen()
    {
        $this['dialog']->handleOpen('deleteImage');
    }
}

Podle dokumentace PHP k callbackům by to fungovat mělo, je toto v Nette zakázané – volat ve formuláři handler jiné komponenty ? Nebo je nějaká chyba v mém kódu či ve Frameworku ?

Druhý dotaz mám jak předat v callbacku onClick[] nějaký parametr – to už je otázka spíš na PHP.

Mám použít něco jako:

$submitButton->onClick[] = [$object, 'method']($parameter);

Nabo nějak nahradit ‚method‘ něčím jako anonymní funkcí či arrow funcí?

Díky všem přdem za rady.

dakur
Člen | 493
+
+1
-

@mbrecher Ad handle – to nejde. Z dokumentace:

Signál se vždy volá na aktuálním presenteru a view, tudíž není možné jej vyvolat na jiném presenteru nebo view.

Týká se to i komponent. Presenter je taky komponenta. Signál (tedy handle) se vždy váže ke své komponentě.

Proč si neuděláš v presenteru handleDeleteAll()?

Editoval dakur (30. 8. 2022 9:19)

mystik
Člen | 289
+
0
-

Správné řešení je to volat přes presenter nebo nějakou nadřazenou komponentu a ne aby se komponenty volaly napřímo.

Jinak k tomu parametru to takhle použít nejde, protože callbacku se parametry předávají v okamžiku volání. Takže je řešení si udělat anonymní funkci.

$submitButton->onClick[] = function() use ($object, $method) { $object->method($parameter); });

Jde totiž o to, že callbacky dostávají parametry od volajícího, které mohou používat. Např.:

$submitButton->onClick[] = function(SubmitButton $submittedBy) use ($object, $method) { $object->method($submittedBy->getCaption()); });

Editoval mystik (30. 8. 2022 10:32)

m.brecher
Generous Backer | 736
+
+1
-

@dakur

Ahoj, díky za komentář, ano s tím, že se signál volá na aktuálním presenteru a view souhlasím a nesnažím se to dělat jinak. Problém nemám s volání signálu, ale s callbackem eventHandleru formuláře $submitButton->onClick[]. V tomto případě odesílám formulář submit tlačítkem „Smazat“, odešle se formulář post metodou jako signál, který je interně ve formuláři zabudován v hidden prvku, ve formuláři se automaticky Frameworkem vykresluje toto:

<input type="hidden" name="_do" value="editImageForm-submit">

Nejsem znalec, ale předpokládám, že položka _do je speciální formulářový signál, který po zpracování zase vykreslí původní presenter a původní view. Takže si myslím, že problém mám jinde než tam, že bych volal signál na jiném presenteru nebo view – ani to asi nejde.

@mystik ve svém komentáři odpovídá přesně na to co je problémem – jak správně volat ten callback.

Ale díky za komentář.

m.brecher
Generous Backer | 736
+
0
-

@mystik

Ahoj, díky za navedení správným směrem – použít pro callback anonymní/arrow funkci – funguje to. Po prozkoumání problému jsem objevil příčinu proč nešlo volat komponentu v callbacku. Mezi automatickým injektováním do callbacku (eventHandleru) a očekávaným vlastním parametrem v komponentě byl konflikt.

Formulář – navěšení callbacku

	$submitButton->onClick[] = [$presenter, 'buttonClickHandler'];

Presenter – callback přijímá parametry dle autowiringu:

	public function buttonClickHandler(ArrayHash $values)  // funguje - parametr je v souladu s autowiringem
    {
		.......
    }

Presenter – callback s vlastním parametrem:

	public function buttonClickHandler(ArrayHash $values, string $myParam)
    {
       .....
			//  Nette vyhodí výjimku, předat vlastní parametr $myParam autowiring neumožňuje
    }

Pro předání vlastního parametru do callbacku je potřeba autowiring obejít:

  1. volat handler přes jinou metodu:
	$submitButton->onClick[] = [$this, 'buttonClickHandlerBridge'];

	public function buttonClickHandlerBridge(ArrayHash $values)	// Nette injektuje
    {
		$myParam = 'deleteImage';
        $this->buttonClickHandler($myParam, $values);  // ručně předáme vlastní parametr + injektovaný
    }

	public function buttonClickHandler(string $myParam, ArrayHash $values)
	{
		...........
	}
  1. anonymní funkce, injektované parametry se předají jako parametry funkce, vlastní parametr pomocí use():
     $submitButton->onClick[] = function(ArrayHash $values) use($myParam) { $this->buttonClickHandler($values, $myParam); };
  1. arrow funkce (nejjednodušší varianta) – předá injektované i vlastní parametry
     $submitButton->onClick[] = fn(ArrayHash $values) => $this->buttonClickHandler($values, $myParam);

Objekt, který voláme v callbacku může být presenter nebo komponenta presenteru:

     $submitButton->onClick[] = fn(ArrayHash $values) => $this['someComponent']->buttonClickHandler($values, $myParam);

Takhle, když si to člověk v hlavě setřídí, tak je to skoro triviální:

  • jako callback lze použít metodu presenteru nebo komponenty presenteru
  • je potřeba respektovat autowiring a nepředávat vlastní parametry
  • v případě, že je potřeba předat vlastní parametr, tak použít arrow funkci