Všechny formuláře ať mají vlastní šablonu

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Jan Endel
Člen | 1016
+
0
-

Zdravím, dělám teď velkou aplikaci a řekl jsem si, že si formuláře nadefinuju každý ve vlastní třídě a chtěl jsem si udělat to, aby každý formulář měl vlastní šablonu přes kterou by se vykresloval.

Jenže ouha, formuláře nejsou ve skutečnosti komponenty, takže nemají k sobě přidrátovaný šablonovací systém. V současnosti jsem dospěl k něčemu takovému. Mám předka pro všechny formuláře a v něm zadefinováno (má protected proměnou template):

public function render()
{
    $templateName = lcfirst($this->name);

    $dir = dirname($this->getReflection()->getFileName());
    $this->template = new \Nette\Templating\FileTemplate("$dir/templates/$templateName.latte");
    $this->template->registerFilter(new \Nette\Latte\Engine);
    $this->template->formName = $this->name;
    $this->template->_control = $this;
    $this->template->render();
}

žel mi to nadává, že komponenta s daným názvem neexistuje. Jdu na to úplně špatně? Jak tohleto vyřešit?

Editoval pilec (26. 2. 2012 17:22)

duke
Člen | 650
+
0
-

A nebude jednodušší udělat si komponentu na renderování formulářů?
Použití v presenteru by pak vypadalo:

	protected function createComponentMyForm($name)
	{
		return new FormRendererControl(new MyForm);
	}

Konstruktor FormRendererControl by pak mohl vypadat takto:

	public function __construct(Nette\Application\UI\Form $form, Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
	{
		parent::__construct($parent, $name);
		$this->addComponent($form, 'form');
	}

Konkrétní cestu k šabloně můžeš injektovat přes setter komponenty, případně ji nějak vypočítávat z $name. A v šabloně bys použil {form form} ... {/form}, případně si to makro nějak přiohnul aby bralo „form“ jako výchozí parametr a tys mohl mít v šabloně jen {form} ... {/form}.

Jan Endel
Člen | 1016
+
0
-

Díky něco podobného mě taky napadlo.

Šaman
Člen | 2659
+
+1
-

Edit: Variantu s komponentou, která obsahuje $form jsem úspěšně používal, ale není to úplně čisté – já chci přiřadit šablonu (resp. renderer) formuláři a ne vytvářet novou komponentu okolo formuláře jenom proto, že ona už se šablonou umí pracovat..


Taky mě to dost trápilo, tak jsem vytvořil FormTemplateRenderer:

<?php
class FormTemplateRenderer extends \Nette\Object implements \Nette\Forms\IFormRenderer
{

  /** @var ITemplate */
  protected $template;

  public function __construct($templateOrFile = NULL)
  {
    if ( $templateOrFile instanceof \Nette\Templating\Template )
    {
      $this->template = $templateOrFile;
    }
    elseif ( is_file($templateOrFile) )
    {
      $this->template = new \Nette\Templating\FileTemplate($templateOrFile);
    }
    elseif ( isset ($templateOrFile) )
    {
      throw new \Nette\UnexpectedValueException('Argument musí být soubor nebo instance template!');
    }
  }

  public function render(\Nette\Forms\Form $form)
  {
    $template = $this->getTemplate();
    $template->onPrepareFilters[] = function($template)
    {
      $template->registerFilter(new \Nette\Latte\Engine);
    };

    $template->_control = $form->presenter;
    $template->form = $form->name;
    $template->render();
  }

  public function getTemplate()
  {
    if( $this->template instanceof \Nette\Templating\Template )
    {
      return $this->template;
    }
    else
    {
      throw new \Nette\InvalidStateException("Není nastavena šablona!");
    }
  }

  public function setTemplate(\Nette\Templating\Template $template)
  {
    $this->template;
  }
}
?>

A používám ho v BaseFormu, který si šablonu dohledává dokonce sám v adresáři s .php souborem

<?php
/**
 * Formulář, který si umí dohledat šablonu
 */
class BaseForm extends \Nette\Application\UI\Form
{
  public function __construct()
  {
    parent::__construct();
    foreach ( $this->formatTemplateFiles() as $file )
    {
      if ( is_file($file) )
      {
        $this->renderer = new FormTemplateRenderer($file);
        break;
      }
    }
    // tady si můžeš zkontrolovat, jestli se dohledala šablona a pokud ne, použít např. DefaultFormRenderer
  }

  /**
   * Vrací pole souborů, které mohou obsahovat šablonu formuláře
   */
  protected function formatTemplateFiles()
  {
    $dir = dirname($this->reflection->getFilename());
    return array
      (
        "$dir/{$this->reflection->name}.latte",
      );
  }

}
?>

Editoval Šaman (26. 2. 2012 21:49)

Jan Endel
Člen | 1016
+
0
-

Tak jsem ten tvůj kód proštudoval, a zjistil jsem, že když u svého původního příspěvku změním:

$this->template->_control = $this;

na

$this->template->_control = $this->presenter;

vše se rozjede.

duke
Člen | 650
+
0
-

@Šaman Hezké řešení.

Šaman
Člen | 2659
+
0
-

@Pilec: Aha, to je věc, která se mi na nových makrech nelíbí. Interně makro potřebuje název formu a komponentu, ve kterém se ten form vyskytuje. Je to proto, zby se dalo v šabloně psát {form 'MujForm'}.....{/form}.
A makro si ten formulář dohledá v tom $_control podle jména.

Daleko raději bych do šablony předával $form = $this. A pak to použil {form $form}....{/form}. Bohužel to ve stávající podobě formulářových maker nejde. (V těch původních co byly v doplňcích toto šlo.)

Editoval Šaman (26. 2. 2012 22:54)

David Grudl
Nette Core | 8218
+
0
-

Šaman napsal(a):

…A pak to použil {form $form}....{/form}. Bohužel to ve stávající podobě formulářových maker nejde.

Fixed

22
Člen | 1478
+
0
-

…jen pro jistotu, imho stačí tedy do potomka Nette\Application\UI\Form, pokud chcete rendreovat šablonu přímo u formuláře:

	...
	public function render()
	{
		$template = new FileTemplate(__DIR__ . '/formTemplate.latte');
		$template->registerFilter(new Engine());
		$template->form = $this;

		$template->render();
	}

je to tak?

Editoval 22 (27. 8. 2012 5:17)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Myslím, že toto je snazší řešení, jak jednoduše svázat formulář s vlastní šablonou: https://forum.nette.org/…-jeho-limity

22
Člen | 1478
+
0
-

V čem je to jednodudušší? Už na první pohled m ito přijde složitější, navíc jako jsem nucen kvůli formuláři vytvářet ještě control..což se mi právě moc nelíbí.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Pokud to dobře chápu, tu tvoji metodu render bych umístil do poděděného UI\Form. Jediný rozdíl je tedy v tom, že nebudu dědit od UI\Form ale od UI\Control, a definici formuláře nedám do attached, konstruktoru nebo nějaké vlastní alá configure metody, ale do createComponentForm. UI\Control má automaticky nachystanou šablonu, podporuje v té šabloně snippety atd… rozdíl množství je kódu je minimální, možná i menší :).

https://gist.github.com/3486768

Editoval vojtech.dobes (27. 8. 2012 10:56)

22
Člen | 1478
+
0
-

viz. výsledek diskuse z Jabberu https://pla.nette.org/…vs-kompozice

Hafran
Člen | 121
+
0
-

@22 Hm, tak z tohohle moc moudrej nejsem – jakej je teda závěr? :) Že je lepší dědit od Container než od Control?

@vojtech.dobes a nemůže se mi rozpadnout v nějakých situacích callback, třeba onSuccess, když bude formulář volat callback z rodičovskýho Control? A když to nejni v attached, nejsou nějaké situace, kde mi to bude dělat neplechu, pač to nebude připojený?

Pardon za hafo otázek :)

22
Člen | 1478
+
0
-

@Hafran: lepší je použít Control

petr.jirous
Člen | 128
+
0
-

Zdravím,
používám toto řešení, které napsal Šaman v příspěvku #4. Funguje to, ale když do šablony formuláře chci dát snippet, tak mi to hází undefined varible: basePath.

Za všechny nápady/připomínky předem díky

Šaman
Člen | 2659
+
0
-

Dneska už bych to řešil jinak – mám pravou komponentu (takže fungují snippety, vytváření šablon apod.) a do ní předávám formulář definovaný pomocí továrničky. Až se najím, tak sem postnu postup, ale už tu několikrát zazněl od Vojty Dobeše.

Řešení s TemplateRendererem stále funguje, ale už ho nerozvíjím, takže ti mohu jen obecně poradit vložit do šablony všechny defaultní porměnné Nette ($basePath, $user, nevím co dalšího), abys dosáhl plné kompatibility vytvořené šablony.