Předání parametru do šablony komponenty – v ajaxovém requestu nefunguje

m.brecher
Generous Backer | 758
+
0
-

Ahoj!

když jsem ajaxoval standardní formulář Nette zabalený do UI\Control, narazil jsem na drobný problém.

předání parametru do šablony formuláře v šabloně presenteru:

{control 'myForm', heading: 'Editační formulář'}  {* zde přenos parametrů v ajaxu NEfunguje *}

šablona formuláře:

<div n:snippet="control" class="base-form">
   {form 'form'}
      {include 'flashBox.latte', formId: $form->getElementPrototype()->id}  {* zde přenos parametrů vždy funguje *}

        <h2 n:ifcontent class="form-heading">{$heading}</h2>  {* $heading je při ajaxu null *}

		{dump $heading}               {* dump ale $heading vypíše správně vždy *}

   {/form}
</div>

komponenta formuláře:

class MyForm extends UI\Control
{
    protected function createComponentForm(): BaseForm
    {
        $form = new BaseForm();

        // .....

        return $form;
    }

    public function render(?string $heading = null): void
    {
        $heading = 'Editační formulář';                 // fix ajax latte chyby
        $this->template->heading = $heading;

		// ...

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

Do šablony formuláře předávám v šabloně presenteru parametr $heading. Při standardním requestu se tento parametr předá bez chyb. Když použiji ajaxový request pro submit formuláře (knihovna Naja), parametr $heading se NEPŘEDÁ. Přitom {dump} v šabloně komponenty vypíše hodnotu $heading SPRÁVNĚ.

Jde to snadno fixnout v metodě render() komponenty, ale bylo by dobré chybu vytrasovat a odstranit. Pokud budeme komponentu formuláře používat opakovaně, potom je optimální nastavovat $heading formuláře právě v konkrétní šabloně.

Díky

Editoval m.brecher (31. 10. 2023 18:31)

mskocik
Člen | 53
+
+3
-

Renderovanie snippetov kompletne obchadza sablonu. A SnippetDriver vola render() na komponente natvrdo.

private function renderChildren(): void
{
    $queue = [$this->control];
    do {
        foreach (array_shift($queue)->getComponents() as $child) {
            if ($child instanceof Renderable) {
                if ($child->isControlInvalid()) {
                    $child->snippetMode = true;
                    $child->render();
                    $child->snippetMode = false;
                }
            } elseif ($child instanceof Nette\ComponentModel\IContainer) {
                $queue[] = $child;
            }
        }
    } while ($queue);
}

Ci uz je to optimalne, alebo by bolo vhodne umoznit nejaku customizaciu, je uz druha vec.

Editoval mskocik (31. 10. 2023 20:02)

Kamil Valenta
Člen | 762
+
+5
-

Ona to není moc novinka, děje se to už tak cca 13 let. A určitě ještě někde koluje video, kde to David Matějka popisuje.
Nepředávej ten parametr ze šablony, ale v parentu komponenty zavolej patřičný setter.

m.brecher
Generous Backer | 758
+
+1
-

@mskocik

Renderovanie snippetov kompletne obchadza sablonu.

Aha, pak už to chápu. Takže se vykreslí šablona komponenty ale hlavní šablona do které je šablona komponenty vložena se nevykreslí. To zní logicky a pak je jasné, že se do šablony komponenty ani nemůže předat žádný parametr z hlavní šablony. Toto by se také mohlo doplnit do dokumentace (pokud to tam někde už není), protože si tohle člověk těžko domyslí.

m.brecher
Generous Backer | 758
+
0
-

@KamilValenta

A určitě ještě někde koluje video, kde to David Matějka popisuje.

Video jsem pečlivě shlédnul, ale cca před rokem a problém lokálních parametrů tam @DavidMatějka skutečně důrazně zmiňoval. Pochopil jsem to ale nějak takto:

{var $param = 'value'}

{snippet 'mySnippet'}
   {$param}  {* no value *}
{/snippet}

Vůbec jsem si to nespojil s předáním parametrů do šablony v tagu {control}. Mrknu do dokumentace, jestli je tam tento aspekt zmíněn a popř. doplním.

David Grudl
Nette Core | 8142
+
0
-

Nepomůže tady snippetArea okolo {control}?

m.brecher
Generous Backer | 758
+
-2
-

@DavidGrudl

Nepomůže tady snippetArea okolo {control}?

Zkouším:

{* přidat snippetArea nepomáhá, $heading se nepředá *}
    {snippetArea 'myFormWrapper'}
        {control 'myForm', heading: 'Editační formulář'}
    {/snippetArea}

To je škoda, protože předávat řídící/vykreslovací parametry do nějaké obecně použitelné latte šablony je výborný způsob jak zpřehlednit a strukturovat kód. Typické použití je:

a) doplnění nějakého nadpisu/popisky formuláře specifického v daném presenteru
b) konfigurace funkce komponenty specificky podle umístění – třeba navigační menu ve výchozím stavu zabaleno/rozbaleno

Dá se to snadno vyřešit předáním parametru do šablony v metodě render() abstraktního předka přes tryCall(‚afterRender‘) metody final potomka. Ale není to ideální místo, pro umístění statického textu do šablony.

V budoucnu by se mohla podpora Ajaxu v komponentách modernizovat a deklarovat Ajax komponenty UI\Control atributem:

#[Ajax]
final MyFormControl extends UI\Control
{
    //.....
}

Atribut #[Ajax] by mohl řídit vše, co se dnes dělá ručně:

a) funkci komponenty – implementace v nějakém abstraktním předku, final potomci by mohli být ajaxoví nebo ne
b) automatické přidání značky pro ajax do signálů komponenty v odkazech, nebo do značky <form> ve formulářích
c) automatické obalení šablony komponenty do snippetu
d) ošetření předání parametru při vykreslování komponenty

Ad d) by se řešilo lépe, když by latte při vykreslování šablony komponenty vědělo, zda používá ajax nebo ne. V případě, že by:

– {control} komponenty obsahoval nějaké parametry,
 – komponenta byla označena atributem #[Ajax]

mohlo by latte zapsat do přeložené šablony informaci, že se má spouštět nadřazená šablona a předat parametry.

Tudy by se mohly ubírat úvahy jak modernizovat ajax v komponentách Nette.

Editoval m.brecher (1. 11. 2023 15:52)

Pepino
Člen | 249
+
+2
-

@mbrecher
Pokud používáš jednu komponentu vícekrát používej multiplier.
Pro předání parameterů komponentě neslouží metoda render.

mskocik
Člen | 53
+
0
-

@mbrecher A nestačí ti používať custom render metódy, https://doc.nette.org/…n/components#… a tam príklad s renderPaginator? Teraz už vieš, že snippety volajú render() a v prípade klasického renderu môžeš definovať props tak.

{control poll}
{control poll:paginator 123, 'hello'}
David Grudl
Nette Core | 8142
+
+3
-

Zkusil jsem do nette/application 3.1-dev přidat podporu pro {control} uvnitř {snippetArea}, aby tak šlo předat parametry nebo volat jinou render metodu.

Ale snippety jsou dost komplexní věc, tak si nejsem jist, jestli jsem tím nic nerozbil.