LogicException Form declaration is missing

- m.brecher
 - Generous Backer | 911
 
Ahoj,
při použití snippetů v šabloně formuláře jsem narazil na takovouto chybu.
Formulář je poděděný z UI\Control, do šablony do proměnné $form natvrdo předávám Form
public function render(): void
{
    $template->form = $this->form;   // Form
    // ...
}
V šabloně formuláře mám blok {define ‚container‘}, který vkládám pomocí {include} a šablona se v pořádku vykresluje do chvíle, kdy značku {include ‚container‘} obalím snippetem:
{snippet 'control'}
   <form n:name="$name">
      .......
      {snippet 'container'}  {* this snippet makes exception *}
          {include 'container'}
      {/snippet}
    </form>
{/snippet}
{define 'container'}
    {do bdump($form)}  {* variable $form exists *}
     <div class="container">
        ......
        {input 'name'}  {* when commented out is OK *}
    </div>
{/define}
Po obalení {include} snippetem vyhodí Latte výjimku:
LogicException
Form declaration is missing, did you use {form} or <form n:name> tag?
Snippet vykresluji pomocí $formControl->renderControl(‚container‘)
Proměnná $form uvnitř bloku existuje, výjimku vyhazuje značka {input ‚name‘}, kterou pokud zakomentuji tak se šablona vykreslí OK. Pokud odkomentuji {input ‚name‘} a zakomentuji {snippet ‚container‘} vykreslí se také OK.
Takže to vypadá, že bude asi nějaká chyba ve značce {input} pokud je v bloku, který se vkládá {include} a ten je uvnitř snippetu.
latte/latte v3.0.18
Editoval m.brecher (12. 10. 2024 13:25)

- Marek Bartoš
 - Nette Blogger | 1313
 
Proměnnou $form ti definuje makro {form formName}, případně
<form n:name='formName'>. Při překreslení snippetu se
makro nevykoná, protože je mimo snippet, a tak uvnitř snippetu proměnná
není dostupná. Řešení je definovat ve snippetu kontext formuláře,
přes {formContext $control['formName']}
Určitě by bylo fajn tohle chování vylepšit. Napadá mě k tomu pár možností:
- ve výjimce uvést informaci o formContext, pokud jsme uvnitř snippetu neobsahujícího form
 - ve snippetu nemít proměnnou automaticky deklarovanou, i když se jedná o ne-ajaxový request (možná to tak už je, netuším)
 - při kompilaci se podívat, zda snippet není uvnitř formuláře a vytvořit formContext interně podle formuláře (to však nebude fungovat vždy, skládání šablon může být dynamické)
 
Proměnná $form uvnitř bloku existuje
I při ajaxovém requestu?

- m.brecher
 - Generous Backer | 911
 
@MarekBartoš
Proměnnou $form ti definuje makro {form formName}, případně <form n:name=‚formName‘>. Při překreslení snippetu se makro nevykoná, protože je mimo snippet, a tak uvnitř snippetu proměnná není dostupná.
Proměnnou $form jsem si do šablony předal natvrdo je k dipozici všude v šabloně (i ajax). Tag {input ‚name‘} ale neumí $form ze šablony použít, bezpochyby proto, že používá jinou „interní“ proměnnou.
Řešení je definovat ve snippetu kontext formuláře, přes {formContext $control[‚formName‘]}
Díky, skvělý nápad, toto funguje:
{snippet 'container'}
    {formContext $form}   {* $form injected to template by hand *}
        {include 'container'}
    {/formContext}
{/snippet}
Z dokumentace Latte byl ale tag {formContext} odstraněn a plugin Latte Pro tento tag označuje jako deprecated takže @DavidGrudl bezpochyby plánuje jeho definitivní zrušení a nahradí ho asi s nějakým lepším řešením.
Výstupem diskuze v tomto vlákně je zjištění, že označením tagu {formContext} jako deprecated vznikl problém ajax snippetů uvnitř formulářů. Pro funkci snippetu uvnitř formuláře je použití tagu {formContext} bezpochyby nutné.
Pokud by se nedařilo nějaké „lepší“ řešení pro snippety ve formulářích najít, mohlo by se řešení s tagem {formContext} ponechat a odebrat mu tag deprecated + dát popis do dokumentace.
Dočasným řešením je překreslovat vždy celý formulář.
Editoval m.brecher (13. 10. 2024 12:37)

- m.brecher
 - Generous Backer | 911
 
@MajklNajt
ja to obchádzam napríklad takto: …
Na vykreslení dat z objektu formuláře např. $flashes to stačí. Na vykreslení formulářových prvků pomocí makra {input} nikoliv, a právě tento případ zde řešíme.
I když si do šablony proměnnou $form předáš ručně tak ti makra {input} ve snippetu i tak vyhodí výjimku – makro {input} neumí použít ručně předanou proměnnou $form. Jediné řešení, na které přišel @MarekBartoš je použít tag {formContext}, který je ale deprecated a bezpochyby bude z Latte v budoucnu vyřazen.

- MajklNajt
 - Člen | 518
 
m.brecher napsal(a):
Na vykreslení dat z objektu formuláře např. $flashes to stačí. Na vykreslení formulářových prvků pomocí makra {input} nikoliv, a právě tento případ zde řešíme.
to je kravina, prekresluje mi to aj formulárové prvky vytvárané cez
{input}, {label}… mám to celé obalené do
snippetArea, ktorú samozrejme tiež invalidujem
EDIT: a teda používam aj {formContainer}, možno preto mi to
funguje
{snippetArea myFormContainer}
	{form myForm, class => "ajax"}
...
		{snippet personal}
			{formContainer personal}
				{foreach $formContainer->getComponents() as $i}
					<p {if $i->hasErrors()}class="input_error"{/if}>{label $i /}{input $i}</p>
				{/foreach}
			{/formContainer}
		{/snippet}
...
		{snippet contact}
			{formContainer contact}
				{foreach $formContainer->getComponents() as $i}
					<p {if $i->hasErrors()}class="input_error"{/if}>{label $i /}{input $i}</p>
				{/foreach}
			{/formContainer}
		{/snippet}
...
		{snippet billing}
			{formContainer billing}
				{foreach $formContainer->getComponents() as $i}
					<p {if $i->hasErrors()}class="input_error"{/if}>{label $i /}{input $i}</p>
				{/foreach}
			{/formContainer}
		{/snippet}
...
	{/form}
{/snippetArea}
					Editoval MajklNajt (15. 10. 2024 12:12)

- m.brecher
 - Generous Backer | 911
 
@MajklNajt
to je kravina, prekresluje mi to aj formulárové prvky vytvárané cez {input}, {label}… mám to celé obalené do snippetArea, ktorú samozrejme tiež invalidujem
Tag {snippetArea} to neřeší, ten řeší snippety v includované podšabloně – což není ten případ, ale:
a teda používam aj {formContainer}, možno preto mi to funguje
toto je správné řesení! Díky moc za důležitou informaci, protože po zabalení problematických tagů {input} do {formContainer} je problém vyřešen:
{snippet 'container'}
    {formContainer 'container'}   {* po přidání {formContainer}  je to OK !!! *}
        {input 'name'}
    {/formContainer}
{/snippet}
Takže deprecated tag {formContext} není nutné používat. V dokumentaci Latte je ale popis funkce {formContainer} nedostatečný:
citace: „S vykreslením prvků uvnitř formulářového kontejneru
pomůže tag {formContainer}“
viz: https://doc.nette.org/…ms/rendering#…,
V dokumentaci nette – ajax snippety tato důležitá informace chybí úplně.
Bylo by určitě vhodné nutnost použití tagu {formContainer} ve vnořených snippetech ve formulářích do dokumentace Nette – Snippety doplnit. Jak ukázalo toto vlákno, ani zkušení vývojáři jako @MarekBartoš nebo @MajklNajt nedokázali napoprvé poradit správné řešení.
Editoval m.brecher (15. 10. 2024 17:00)

- Marek Znojil
 - Člen | 92
 
Ahoj, pamatuji si, že jsem řešil něco podobného a napadlo mě, že bys mohl zkusit nahradit:
{snippet 'container'}
    {formContainer 'container'}   {* po přidání {formContainer}  je to OK !!! *}
        {input 'name'}
    {/formContainer}
{/snippet}
Za:
{snippet 'container'}
        {input $form['name']}
{/snippet}
Předáš tagu rovnou instanci komponenty. A pokud ti to projde, tak si myslím, že je problém přímo v tagu {input}.
Raději jsem ještě narychlo zkusil simulaci a dle všeho to tak vypadá:
{form form, class: 'ajax'}
	{snippet form}
		{var $form = $control['form']}
		{input $form['name']} // projde
		{input $form['send']}
	{/snippet}
{/form}
{form form, class: 'ajax'}
	{snippet form}
		{var $form = $control['form']}
		{input name} // Form declaration is missing, did you use {form} or <form n:name> tag?
		{input $form['send']}
	{/snippet}
{/form}