Nefunguje redrawControl v komponentě

Polki
Člen | 553
+
0
-

Zdarec.

mám komponentu:

namespace App\AppModule\Components;

use Nette\Application\UI\Control;
use App\Forms\BasicFormFactory;

class MyComponent extends Control {

    /** @var BasicFormFactory */
    private $basicFormFactory;

    public function __construct(
            BasicFormFactory $basicFormFactory) {
        parent::__construct();
        $this->basicFormFactory = $basicFormFactory;
    }

    public function renderBasic() {
        $this->template->render(__DIR__ . '/templates/basic/default.latte');
    }

    public function createComponentBasicForm() {
        return $this->basicFormFactory->create([$this, 'basicFormFactoryCallback']);
    }

    public function basicFormFactoryCallback($form, $values) {
        if ($this->presenter->isAjax()) {
            $this->redrawControl('basicFormFactory');
        }
    }
}

šablona ‚/templates/basic/default.latte‘:

{snippet basicFormFactory}
	{control basicForm}
{/snippet}

v šabloně v presenteru volám komponentu:

{control myComponent:basic}

Komponenta se vykreslí jak má. Ale po ajaxovém odeslání formuláře se nepřekreslí snippet, i když metoda ‚basicFormFactoryCallback‘ proběhne.

Nápady?

David Matějka
Moderator | 6445
+
+2
-

je to podobny problem jako se snippety v inkludovane sablone, takze muzes pouzit snippetArea. ale lepsi je se vyhnout jinych render metod u komponent. vice viz https://www.youtube.com/watch?…

Polki
Člen | 553
+
0
-

@DavidMatějka Díky s includovanou šablonou to znám a napadlo mě, že by to mohlo být tak i v tomto případě, ale řekl jsem si, že asi ne, jelikož u klasické render() metody vše funguje jak má.

Každopádně díky.

Tvé vide jsem již před časem viděl.

Polki
Člen | 553
+
0
-

@DavidMatějka Ještě jen chci říct, proč používám jiné render metody. Komponenty, které používám mají vlastní JS, popřípadě CSS kód. Normálně bych tento uložil do bloku head, nebo scripts, ale šablony v komponentách nepodporují bloky, takže potřebuji načíst bloky jinak a nechci to cpát do šablony v presenteru, jelikož by ta šablona pak rostla na obří rozměry.

Proto jsem se vydal cestou jiných render metod, aby celá komponenta byla soběstačná a když načtu komponentu, tak abych nemusel myslet na to, že musím něaký kód, co k ní patří kopírovat někam jinam. Jen prostě načtu jiný render z té komponenty.

Polki
Člen | 553
+
0
-

@DavidMatějka

Tak z nějakého důvodu to nefunguje ani se snippetArea.

přesně podle videa a snippet se vůbec ze serveru neodešle.

David Matějka
Moderator | 6445
+
0
-

ukaz kod

Polki
Člen | 553
+
0
-

@DavidMatějka OK tady:

sablona presenteru:

{block content}
	{control komponenta}
{/block}

{block scripts}
	{include parent}
	{snippetArea scriptsSA}
		{control komponenta:skripty}
	{/snippetArea}
{/block}

v presenteru mám klasický createComponentKomponenta() a vracím tam instanci komponenty.

Komponenta.php:

<?php

namespace App\Components;

use Nette\Application\UI\Control;

class MyComponent extends Control {

    public function render(){
        $this->template->render(__DIR__ . '/default.latte');
    }

    public function renderSkripty() {
        $this->template->render(__DIR__ . '/scripts.latte');
    }

    public function handleColorchange() {
		$this->getPresenter()->redrawControl('scriptsSA');
        $this->redrawControl('slider');
    }
}

no a pak scripts.latte:

<script n:snippet="slider">
	$('.slider').slider(); // Toto je externí knihovna na slider
</script>

toto udělá, že při vyvolání handle metody handleColorchange() se metoda zavolá, snippety se invalidují, ale do prohlížeče se nic neodešle.

Kdyby bylo potřeba ještě nějaký kód tak napiš.

Editoval Polki (12. 9. 2019 19:57)

Polki
Člen | 553
+
0
-

Do prohlížeče přijde jen toto:
[„invalidSnippets“, [[„flashes“, true], [„scriptsSA“, false]], 1]

Nebo v Nette 2.4. už snippetArea nefunguje?

Phalanx
Člen | 310
+
0
-

@Polki Uvažuješ chybně – javascript musíš překreslovat tak, že ho zavoláš v JS a ne tak, že překreslíš nějaký snippet (ten JS kód se ti nevykoná po překreslení snippetu).

Používáš nette ajax knihovnu? Pokud ano, měl bys po success události znovu inicializovat slider nějak takhle:

<script>
$.nette.ext({
	success: function (payload) {
		$('.slider').slider();
	}
});
</script>

Edit: já uvažuju blbě :)

Editoval Phalanx (5. 10. 2019 19:58)

Polki
Člen | 553
+
0
-

@DavidMatějka verze latte: 2.4

Polki
Člen | 553
+
0
-

@Phalanx ehm jo vykoná. Když udělám to samé, ale místo:

{block scripts}
    {include parent}
    {snippetArea scriptsSA}
        {control komponenta:skripty}
    {/snippetArea}
{/block}

napíšu

{block scripts}
    {include parent}
    {snippet scriptsSA}
        {control komponenta:skripty}
    {/snippet}
{/block}

a invaliduju stejně jako výše, tak se to zavolá normálně se překreslí snippet a zavolá se ona funkce, která mi opět inicializuje js přesně, jak má. Je to způsobeno tím, že je to js příkaz ne funkce, takže to se vykoná vždy při překreslení. O tom je js. :)

Pravdu však máš v tom, že to, co jsi napsal je lepší řešení.

Problém je, že stejným způsobem řeším formuláře v modálních oknech a chci, aby se překreslila jen část uvnitř modálního okna, tedy formulář, ne celé modální okno, jelikož překreslení celého modálního okna si nerozumí s knihovnou jQueryModal…

Problém, co tu je je ten, že když je to obaleno ve snippetArea, aby se odeslal jen snippet uvnitř komponenty a její jiné metody render, tak se latte kód vykoná na straně serveru, to mám otestováno, ale do prohlížeče ten překreslený snippet vůbec nedorazí viz příspěvek výše (sleduju, co se posílá ze serveru na klienta).

EDIT 1: Ano, používám nette.ajax.js od Dobeše. :)
EDIT 2: Navíc často potřebuji aplikovat dané skripty jen na onu překreslenou komponentu a nikde jinde. Například, když skript nastavuje pro komponentu nějaký časovač, po kterém spustí událost. V takovém případě by napsání js skriptu obecného a jednoduchého zavolání znamenalo, že pro všechny ostatní komponenty by se přidal další časovač a akce by se spouštěly jinak a nepravidelně. Tedy bych musel ten JS skript napsat a napsat k němu rozšíření, které jsi popisoval výše a v něm by se muselo zjišťovat, která komponenta vyvolala js událost, podle toho vykonat ten js kód jen pro danou komponentu atd.
Jednodužší mi prostě přijde, že ten js skript napíšu jednou, obalím ho do snippetu a ten invaliduju při překreslení komponnty, takže script ví přesně ke které komponentě má přidružit časovač.

Editoval Polki (5. 10. 2019 19:41)

Polki
Člen | 553
+
0
-

Tak pro všechny Nette kouzelníky, včetně @Phalanx a @DavidMatějka . Mám vyřešeno.

Řešením bylo, že při volání metody ‚$this->redrawControl();‘ nad komponentou se vykonala v komponentě jen metoda ‚render()‘. Tedy jinými slovy byl to problém, který popisoval @DavidMatějka , ale s tím rozdílem, že při zavolání redrawControl a tedy následným ‚jiným vykreslováním‘ šablon došlo k tomu, že se šablona nastavená v jiné render metodě vůbec nevykreslila, tedy vykonal se její kód, ale latte ji vůbec nebralo v potaz a tím pádem její snippety vůbec neodeslalo nette v payloadu. (snippetArea tomu vůbec nepomohla)

tedy jsem se rozhodl na ‚snippetArea‘ úplně zapomenout a s nově nabytými informacemi, že při renderování snippetů bere latte v potaz jen template, který je definován v klasické metodě ‚render()‘ jsem vytvořil následující funkční kód:

Šablona komponenty (renderAnother) (another.latte):

<article>
    <h3 class="mt-5">Konečně funkční redrawControl v komponentě</h3>
    {snippet mySnippet}
        <p>{$text}</p>
    {/snippet}
    <a n:href="change!" class="ajax">change text</a>
</article>

Šablona komponenty (renderDefault) (default.latte):

<article>
    <h3 class="mt-5">Test komponent</h3>
</article>

Komponenta:

<?php

namespace App\Components\Calendar;

use Nette\Application\UI\Control;

class Calendar extends Control {

    private $text;

    private $fieldPath;


    public function __construct() {
        parent::__construct();
        $this->text = 'Who let the dogs out';
        $this->fieldPath = [$this, 'renderDefault'];
    }

    public function render() {
        ($this->fieldPath)();
    }

    public function renderDefault() {
        $this->template->render(__DIR__ . '/default.latte');
    }

    public function renderAnother() {
        $this->template->text = $this->text;

        $this->template->render(__DIR__ . '/another.latte');
    }

    public function handleChange() {
        $this->text = 'Me!!!';

        $this->redrawControl('mySnippet');

        $this->fieldPath = [$this, 'renderAnother'];
    }
}

Šablona presenteru:

{block content}
    {control calendar}
    {control calendar:another}
{/block}

@DavidGrudl Nešlo by to nějak efektivněji? Například novým parametrem metody ‚redrawControl()‘ uvnitř komponenty, podle kterého by latte poznalo, ze které šablony/render metody má brát invalidované snippety?

EDIT 1: Problém taky bude, když budu chtít invalidovat snippety ve více, než jedné render metodě. Fuh.

Editoval Polki (8. 10. 2019 16:27)

teekey99
Člen | 45
+
0
-

@Polki to setování render method jako callback, kdy se spolíháš na to, že to je pak zavolaný defaultním renderem, mi nepřijde správný.

Nepomohlo by, vytvořit nadřazenou komponentu, nazvěme ji třeba CalendarRenderer, která obstará snippety a komponenta Calendar vyrenderuje text, kterej ji přijde shora?

Calendar

<?php

namespace App\Components\Calendar;

use Nette\Application\UI\Control;

class Calendar extends Control
{

	private $text;

    public function render(string $template = 'default'): void
	{
		$this->template->text = $this->text;
        $this->template->render(__DIR__ . "/$template.latte");
    }

	public function renderAnother(): void
	{
		$this->render('another');
	{

	public function setText(string $text): self
	{
		$this->text = $text;
		return $this;
	}
}

A potom nadřazená control.

CalendarRenderer:

<?php

namespace App\Components\Calendar;

use Nette\Application\UI\Control;

class CalendarRenderer extends Control
{

    public function render(): void
	{
        $this->template->render(__DIR__ . '/path/to/template.latte');
    }

    public function handleChange(): void
	{
        $this['calendar']->setText('another text');
		$this->redrawControl('another');
    }

	protected function createComponentCalendar(): Calendar
	{
		return (new Calendar())->setText('default text');
	}
}

Template:

{snippet default}
  {control calendar}
{snippet/}
{snippet another}
  {control calendar:another}
{snippet/}

Presenter template:

{control calendarRenderer}
Polki
Člen | 553
+
0
-

teekey99 napsal(a):

@Polki to setování render method jako callback, kdy se spolíháš na to, že to je pak zavolaný defaultním renderem, mi nepřijde správný.

Mě taky ne proto se tu ptám jak na to

Nepomohlo by, vytvořit nadřazenou komponentu, nazvěme ji třeba CalendarRenderer, která obstará snippety a komponenta Calendar vyrenderuje text, kterej ji přijde shora?

Pomohlo a když jsi to tak napsal, tak o vypadá jako daleko lepší řešení. Jediné, co se mi nelíbí je, že komponenty se na sobě starají závislé a udělal jsem tím jakoby sestry. Prostě abych mohl použít jednu, musím k tomu dělat ještě druhou a tím pádem víc tříd, víc service, víc všeho.

A ač to i mě přijde jako lepší řešení není to vlastně jen převedení problému pod jiný hábit? Místo aby jsem se o volání těch metod spoléhal na default render, tak se spoléhám na nějakou jinou, záhadnou komponentu. Princip mi přijde stejný, jen místo metody, co se o to stará se o to stará komponenta.

A když nad tím teď přemýšlím, tak vlastně jsem tím z té komponenty s částmi udělal nějakého neschopného mrzáka, o kterého se musí starat jiná komponenta a tím tak trochu zabíjím znovupoužitelnost ne? Jinak když někdo tu moji komponentu vezme, tak nebude fungovat. Musí k ní přidat i tu druhou, co se o ni stará. Když to bude řešit ta render metoda, tak prostě vezmu komponentu a dám ji jinam a neřeším nic dalšího.

A myslím, že z definice komponenty by měla komponenta být samostatná znovupoužitelná část programu. Tedy by se o to renderování měla komponenta starat sama ne?

teekey99
Člen | 45
+
0
-

@Polki Díváš se na to trochu špatně.

Na tý komponentě není vůbec nic záhadnýho. Je to kompozice dvou komponent, kde každá se stará i jinou část vykreslení dat. To je naprosto v pořádku. Snažit se za každou cenu dělat komponentu takovou, aby obsáhla všechno je špatně. Snaž se komponenty držet co nejjednodušší, úplně stejně jako modely, každá dělá pokud možno jednu věc a kompozicí dosáhneš výsledku.

To, že je nějaká komponenta tvz. „hloupá“, tzn. že shora přijme data a stará se jen o to, jak se ta data vykreslí a jiná, nadřazená komponenta se zas stará o to, jak se tam ty data dostanou, je krásnej příklad separation of concerns a velmi dobře se to pak udržuje a především taky testuje, což je z dlouhodobýho hlediska dobře.

Ta komponenta je znovupoužitelná, vždycky ji budeš používat přes CalendarRenderer a je jedno jestli v další komponentě nebo presenteru. Ta kompozice v sobě zapouzdřuje všechno, co pro je pro funkčnost potřeba.

Polki
Člen | 553
+
0
-

teekey99 napsal(a):

@Polki Díváš se na to trochu špatně.

Na tý komponentě není vůbec nic záhadnýho. Je to kompozice dvou komponent, kde každá se stará i jinou část vykreslení dat. To je naprosto v pořádku. Snažit se za každou cenu dělat komponentu takovou, aby obsáhla všechno je špatně. Snaž se komponenty držet co nejjednodušší, úplně stejně jako modely, každá dělá pokud možno jednu věc a kompozicí dosáhneš výsledku.

Rozumím. Kompozice dvou komponent jsou v pořádku. Z mého pohledu nemá komponenta dělat vše, ale jen jednu věc. To je dobře. Jen by tu věc měla být schopna udělat sama. Ne aby na to potřebovala pomocníka. Když například budu mít košík, tak bude jedna komponenta košík, která bude se starat o logiku košíku a pak bude další komponenta položka košíku například atd. Tady jde taky o kompozici, kde pro správné fungování jsou potřeba komponenty obě. Je zde ale rozdíl v tom, že rodičovské komponentě je úplně jedno, jakou položku košíku dostane a taky kde je uložená. Jinými slovy, košík je sám o sobě dobře testovatelný, položka košíku taky, ale zároveň košík může mít úplně jinou položku košíku a položka košíku může jít do jiného košíku a tedy jsou každá znovupoužitelná zvlášť a nepotřebují jedna druhou k životu, ale je jim jedno, jaká sestra bude.

To, že je nějaká komponenta tvz. „hloupá“, tzn. že shora přijme data a stará se jen o to, jak se ta data vykreslí a jiná, nadřazená komponenta se zas stará o to, jak se tam ty data dostanou, je krásnej příklad separation of concerns a velmi dobře se to pak udržuje a především taky testuje, což je z dlouhodobýho hlediska dobře.

Viz košík a položka košíku. To je v pořádku.

Ta komponenta je znovupoužitelná, vždycky ji budeš používat přes CalendarRenderer a je jedno jestli v další komponentě nebo presenteru. Ta kompozice v sobě zapouzdřuje všechno, co pro je pro funkčnost potřeba.

Pořád platí, že mám 2 komponenty, které vlastně řeší jednu logiku a jsou na sobě závislé do takové míry, že je nemohu použít odděleně není pravda?

Příklad:

Mám komponentu A, která tak jak jsi psal má v sobě obsluhu jiné. Ano, ta obsluha může být v presenteru nebo v jakékoliv jiné komponentě, takže dceřinnou komponentu můžu použít samostatně. Problém nastává tady:

default.latte

...
<a n:href="change!"></a>
...

komponenta.php

public handleChange() {
	...
	$this->getParent()->redrawControl('scripts'); // Toto je hlavní problém ne? Aby se komponenta překreslila musí volat ze svého rodiče metodu redraw control a tak nějak skrytě doufat, že rodič má nadefinovaný tento snippet. Jenže problém je, že ostatní programátoři bývají často dost dummy, nebo si nepřečtou dokumentaci a já kdo vím co. A v tom případě prostě komponenta fungovat nebude, jelikož není v rodiči snippet, která má, nebo jak mohu vědět, že si někdo moji komponentu vytvoří nějak jinak a tedy se nenastaví rodič? Takováhle závislost na rodiči aby mě překreslil mi nepřijde moc znovupoužitelná ani nijak rozumná.
	...
}

Tím jsem prostě tu znovupoužitelnost úplně zabil

Editoval Polki (18. 10. 2019 18:25)

teekey99
Člen | 45
+
0
-

@Polki Je to úplně stejnej princip, jako s tím košíkem. Proč bys z child komponenty volal parent komponentu, aby něco udělala? To nedává smysl.

Komponenta A obdrží data a vykreslí je. Komponenta B pošle data. To je celý, jestli to je pak ve snippetu, reaguje to na signál atd. to už jde přece mimo, to je implementační detail, kterej vůbec ta komponenta neopotřebuje znát.

Kromě toho, čím jsi zabil znovupoužitelnost? U tý Calendar komponenty máš prostě nějakej text a ta ona vykreslí a je jí úplně jedno, co a jak ji ten text pošle. Její public API umožňuje nasetovat hodnotu property $text, tím pádem ji můžeš použít naprosto kdekoliv.

teekey99
Člen | 45
+
0
-

A když už teda potřebuješ, aby v nějaký fázi něco vykonala parent komponenta, tak si můžeš do child komponenty nastavit callback, kterej bude volat a opět – máš ji krásně odstíněnou a je jí jedno, co se v tom callbacku děje.

Polki
Člen | 553
+
0
-

@teekey99 Asi jsme se nepochopili.

Když je komponenta A CalendarRenderer, která má v sobě toto:

{snippet default}
  {control calendar}
{snippet/}
{snippet another}
  {control calendar:another}
{snippet/}

A v .latte souboru v komponentě calendar je signál, který má za úkol překreslit část komponenty calendar a její scripty, aby se aplikovaly znovu, tak v komponentě calendar v souboru default.latte bude toto např:

...
<a n:href="change!" class="ajax"></a>
...

Jenže tento signál může odkazovat pouze do komponenty calendar, ne do CalendarRenderer.

Jenže jelikož renderovací logika je v CalendarRenderer, což je rodič od Calendar, tak musím v calendar komponentě v handle metodě zavolat toto:

public handleChange() {
    ...
    $this->getParent()->redrawControl('scripts');
    ...
}

A tím je znovupoužitelnost úplně mrtvá, jelikož se Calendar stala závislá na předkovi a musí doufat, že předek je nastaven a že má v sobě snippet scripts atd.
Pokud se vykašlu na to, co jsem napsal, ale udělám si jak říkáš callback, který by měl být v každé komponentě, protože co kdyby se při nějakém signálu chtělo volat něco z rodiče, tak jsem na úplně tom samém problému, jelikož když prostě nikdo ten callback nenaplní, tak tam nic nebude očividně a tedy se nezavolá žádné překreslení a toto:

public handleChange() {
    ...
    $this->parentCallbacks();
    ...
}

nebude mít vůbec žádný efekt, jelikož pole ‚parentCallbacks‘ bude prázdné a jsem tam kde jsem byl…

Proto zastávám názor, že komponenta by se o své vlastní vykreslování a redraw při signálech měla starat sama. Od toho vlastně snad už od přírody má render metody a možnost volat ‚$this->redrawControl()‘.

Nebo se pletu?

Editoval Polki (18. 10. 2019 18:51)

Polki
Člen | 553
+
0
-

Jo a není to to samé jako Košík a PolozkaKosiku, jelikož košík se o svoje redraw, vykreslení obsluhu signálů atd stará sám a úplně stejně se o své redraw, render apod stará sama i PolozkaKosiku

to se u Calendar říct nedá, jelikož je závislá na tom, a musí doufat, že ji překreslí rodič a to je z mého pohledu špatně.

Polki
Člen | 553
+
0
-

Nicméně pořád platí že mi to přijde hezčí než to moje.

Takže díky za odpověď. :-)

teekey99
Člen | 45
+
0
-

@Polki

Překreslování skriptů tímhle způsobem je docela prasečina (pardon za ten výraz). Skripty bys měl re-inicializovat na klientovi, když to je potřeba. Nástroje k tomu existují a nemusíš pak přesně tohleto řešit.

Když už teda, je nějakej konkrétní důvod, proč to potřebuječ mít v jedný komponentě s různými render metodami, když stejně kadžá renderuje jinou template? Tak každou render metodu extrahujou do samostatný komponenty, která si každá bude překreslovat svůj snippet a nad nima vytvoř jen rendrovací komponentu, která je vykreslí společně tam, kde je potřebuješ, ale už jim nic shora nebude posílat. Šlo by?

Polki
Člen | 553
+
0
-

Jistě že šlo. Ale jde o ten samý problém.

Jako udělat jde všechno. Otázka je jak by to bylo správně.

Jako nejde právě jen o skripty. Kdyby šlo jen o skripty, tak si na to napsat nějaký js Ajax rozšíření pro nette.ajax.js je easy, i když místo jednoduché věci jako druhá render metoda musíš psát spousty balastu, vracet nějaký identifikátor komponenty, aby bylo jasno která se má prekresli atp, ale to se pominout dá, že místo 15 řádku napíšeš 200.

Jenze ono jde v rámci té jedne logické komponenty o více součástí, které prostě patří k sobě. A komunikuji spolu. Ano, každá má jinou šablonu a vykresluje se v ní něco jiného. Ale jsou úzce spojeny jakože mají stejné signály, stejně akce, stejne kódy na pozadí prostě. A když se něco změní v jedné části musí se to změnit i v té druhé, ale jednotlivé šablony jsou v rodičovské šabloně možno vykreslit na jiných místech. Takže to nemůže být v jedné šabloně jedné render metody, jelikož by nastalo to, že by byly vykreslený ty části u sebe což je nežádoucí.

Zároveň když to rozdělí do více komponent, tak vlastně více komponent bude kromě render metody a šablony ten samý kód a tím pádem vzniká duplicita kódu a to obrovská.

Řešení je nějaká parent komponenta ve které se bude podle toho, co chci vykreslit switchovat v render metode šablona pomocí if, else, ale to je vlastně to řešení co jsem napsal já, akorát místo dlouhosahleho ifovani jsem zvolil metodu callbacku, který je efektivnější.

Polki
Člen | 553
+
0
-

No a jinak kdyby to bylo rozbít do více komponent, tak bych došel do bodu, že když se změní data v šabloně 2, tak se musí určitým způsobem přepsat i data v šabloně 1 takže by musela nějak komponenta 2 donutit komponentů 1 aby se prekreslila a to je zase jiný problém jen jsem je přenesl na jinou část kodu

teekey99
Člen | 45
+
0
-

@Polki

Přiznám se, že už se v tom docela ztrácím. Nemůžu se zbavit dojmu, že máš prostě chybnej návrh toho, jak ty komponenty mají fungovat a jak mají být strukturovaný.

Jestli se potřebuješ zbavit duplicit ve dvou komponentách, tak udělej nějakou abstraktní nebo extrahuj společnou část do trait a každá komponenta už se bude starat jen o renderování příslušný template.

Tím to asi ukončim, protože, už dál nevím, jak bych poradil :)

Polki
Člen | 553
+
+1
-

Rozumím
A opravdu si pomoci cením.
Děkuji

Kori
Člen | 73
+
+2
-

Ahoj, uz je brzy rano, tak mi to uz moc nemysli ;-), ale ten prvni priklad mi moc nedava smysl. Po zavolani redrawControl() v komponente se vola render(), takze by ti to melo spadnout na chybu.

Ten druhy priklad uz mi dava vetsi smysl a pokud spravne chapu, co potrebujes, tak by to slo jeste vyresit pres eventy, pokud tedy nechces dedit a nebo mit vice komponent v kompozici. A i ta chybejici metoda render() v komponente by se tim vlastne dala osefovat :-), viz priklad. O preklesleni se pak, samozrejme, musi postarat nadrazena komponenta / presenter. Netvrdim, ze je to nejlepsi reseni, ale pres eventy je to IMHO o dost elegantnejsi nez volat parenta s predem danym nazvem snippetu, apod.

Kdyz bych tedy kod upravil nejak takhle, melo by ti to jet v ramci jedne komponenty…

Komponenta:

<?php

namespace App\Components;

use Nette\Application\UI\Control;

class MyComponent extends Control {

	public $onChange;

    public function renderA(){
        $this->template->render(__DIR__ . '/default.latte');
    }

    public function renderB() {
        $this->template->render(__DIR__ . '/scripts.latte');
    }

    public function handleColorchange() {
       $this->onChange();
    }
}

Sablona presenteru:

{block content}

{snippet a}
	{control komponenta:a}
{/snippet}

{snippet b}
	{control komponenta:b}
{/snippet}

{/block}

Presenter:

public function createComponentKomponenta() {
        $control = $this->komponentaFactory->create();

		$control->onChange[] = function () {
			if ($this->isAjax()) {
				$this->redrawControl('b');
				//$this->redrawControl('a'); bude fungovat taky


			}
		};

		return $control;
    }
Polki
Člen | 553
+
0
-

@Kori Jasně. To je řešení. No a funguje. Ne že ne. A dobře.

Jenže jak zajistím, že kdo tu moji komponentu bude používat někde nebude trouba a bude plnit ‚$onChange‘ property?
Mám mu vyhazovat vyjímku, nebo to napsat do dokumentace a spoléhat se na to, že nebude programátor natolik líný, aby si ji přečetl celou?

Navíc, kdo chce komponenty, o které se musí ještě sám starat?

Já jsem myslel, že by se komponenta o své překreslování měla starat sama. Ostatně pokud se nepletu, tak proto má taky metody ‚redrawControl‘ ne?

Nebo mi něco uniká? Taky už se v tom začínám ztrácet :D

EDIT 1: pokud tím prvním myslíš to řešení, co jsem postl já nahoře s voláním správné render metody pomocí callbacku v private property, tak to funguje bez problémů taky. Právě proto, když se pídíš jak blázen jak funguje vykreslování komponent po zavolání ‚redrawControl()‘ no a taky znáš jak funguje PHP krásně, tak vytvoříš podobné zvěrstvo jako já, které funguje, ale je to tak hnusný hack, až se za to člověk stydí. :D
Takže se mi vůbec nelíbí a myslím si, že by to tak být udělané vůbec nemělo.

Editoval Polki (20. 10. 2019 1:06)

Kori
Člen | 73
+
+1
-

@Polki Mel jsem na mysli uplne prvni prispevek.

Jinak, me tohle omezeni komponent taky vadi, pac mam taky rad komponenty, ktere zastresuji veskerou funkcionalitu a vice render method pouzivam dost casto.

Ad „starani“ se o komponentu: Mno, ja mam hrozne moc veci resenych pres eventy, v podstate vse napr. co se tyka interakce s uzivatelem jako flashmessage, redirecty, apod., nikdy primo v komponentach neresim a mam to osefovane v createComponent metode, kde komponentu pouzivam. Takze mam treba eventy u CRUDu onAdd, onEdit, onDelete, atp. a v kazdem presenteru se pak muze po vykonani akce chovat aplikace uplne jinak, poslat jine hlasky, atd. To, ze takove eventy existuji si samozrejme musi programator nestudovat a pak spravne pouzit, nebot jsou soucasti rozhrani komponenty ;-) Stejne jako ti napr. Nette formulare vyhazuji onSuccess.

A tu onChange property nebude plnit nejakej trouba, ale tvoje komponenta :-) Trouba se pak jen musi postarat, aby potom aplikace udelala to, co potrebuje, jakmile se ten event zavola.

Editoval Kori (20. 10. 2019 2:00)

Polki
Člen | 553
+
0
-

@Kori Jasně, to je i standardní chovaní, které by komponenty měly mít, že každá bude přijímat nějaké callbacky, které se po určité akci zavolají.

Jak říkáš tím pádem komponenta není nijak natvrdo s ničím spojená a dá se přemístit kamkoliv, protože redirecty a flashe se nastavují někde jinde.

Toto je ale právě specifický případ, který nemyslím, že je úplně očividný a zažitý, když komponenta spoléhá ne na to, že si ošéfuje rodičovská komponenta redirect, ale i překreslení komponenty. To prostě není standard a nemůžu se pak divit programátorům, kteří tu mou komponentu budou používat a stráví půl dne naštvaní nad tím, že se jim to nepřekresluje, než si konečně kvůli nestandardnímu chování přečtou dokumentaci, kde se dozví, že to je jejich práce.