LogicException Form declaration is missing

m.brecher
Generous Backer | 845
+
0
-

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. 13:25)

Marek Bartoš
Nette Blogger | 1249
+
0
-

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 | 845
+
-1
-

@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. 12:37)

MajklNajt
Člen | 490
+
0
-

ja to obchádzam napríklad takto:

{snippet errors}
	{var $form = $control["myForm"]}
	<div n:ifcontent class="flashes">
		<div n:foreach="$form->errors as $error" class="flash error">{$error}</div>
	</div>
{/snippet}
m.brecher
Generous Backer | 845
+
-1
-

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

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. 12:12)

m.brecher
Generous Backer | 845
+
0
-

@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. 17:00)

Marek Znojil
Člen | 82
+
0
-

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}