Vlastní formulářové komponenty

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

Ve vedlejší sekci David probírá jak kdo vytváří vlastní formulářové komponenty. Já teď v podstatě řeším něco podobného jen jsem si to ještě zesložitil.

Jak už jsem psal v jednom tématu řeším jak udělat dynamicky vygenerovaný formulář který by obsahoval rozličné sady formulářových prvků.

Jednoduše si v administraci poskládám jak by měl daný formulář pro položku vypadat. Může byt sestavený s jednoduchým formulářových elementů co obsahují jen jeden input a nebo ze složitějších kde jsou třeba 3 textové inputy, 1 select box atd.

Už s tím laboruju delší dobu, ale nedaří se mi najít nějaké řešení co by bylo asi nejlepší. V současné verzi jsem to vyřešil tak že každý formulářový element je poděděný \Nette\Forms\Container a má v sobě metodu co tomuto kontejneru vytvoří jednotlivé prvky:

class NameFormElement extends \Nette\Forms\Container
{
	public function createControls()
	{
		// Get form instance
		$form = $this->getForm();

		// Create elements inputs
		$this->addText('value', 'Value');

		// Check if this element is requuired
		if ( $this->template->getParam('required') ) {
			$this['value']
				->addRule($form::FILLED);
		}

		if ( $this->itemEntity ) {
			$this['value']
				->setDefaultValue($this->itemEntity->title);
		}

		return $this;
	}
}

Takže když si vytvářím celý formulář tak si načtu jaké elementy má obsahovat a v cyklu je vytvořím jako kontejnery:

class ServiceForm extends \Nette\Application\UI\Form
{
	/**
	 * Configure item form
	 *
	 * @return ServiceForm
	 */
	public function configure()
	{
		// Elements
		$this->addContainer('elements');

		// Attach all elements
		foreach($this->typeEntity->getElementsInTemplate($this->templateEntity) as $element) {
			$elementGroup = $element->element->group;
			$elementGroup = explode('_', strtolower($elementGroup));
			$elementGroup = implode('', array_map("ucfirst", $elementGroup));

			$elementType = $element->element->type;
			$elementType = explode('_', strtolower($elementType));
			$elementType = implode('', array_map("ucfirst", $elementType));

			$elementClass = '\\App\FormElements\\Elements\\'. $elementGroup .'\\'. $elementType .'FormElement';

			// Repeatable element
			if ( $element->element->group == 'form' && in_array($element->element->type, array('date', 'email', 'link', 'text', 'textarea')) ) {
				$this['elements']
					->addDynamic($element->id, array($elementClass, 'factory'), 1);

			// Non-repeatable element
			} else {
				// Create element container
				$container = new $elementClass();

				// Add container to form
				$this['elements'][$element->id] = $container;

				$container
					// Add element to container
					->setElement($element)
					// Add edited item to container
					->setItem($this->serviceEntity)
					// Create element controls
					->createControls();
			}
		}

		return $this;
	}
}

Zatím to mám trochu na prasáka ;) ale myšlenka je ta, že si u jednotlivých elementů zjistím z jaké jsou skupiny (core, form, media, social, atd.) a co to je vlastně za element (input, link, email, image, atd.) Z těchto informaci si sestavím název třídy co tvoří daný kontejner, kontejner vytvořím, přiřadím do formu a zavolám na něm createControls což přidá jednotlivé formulářové prvky (input, selet, checkbox) danému kontejneru.

Kontejneru ještě pošlu taky onen element a editovanou položku. Z elementu zjistím ještě nějaké další nastavení (zda je to povinné, limity, atd.) a z editované položky pak vložím defaultní hodnotu konkrétním políčkům.

No a jak jsem dneska pročítal topic na linku výše, přemýšlím zda jsem šel správnou cestou, zda to neřešit přes samotné controly? Ale tam se mě zase nelíbí že se musí ty jednotlivé inputy dělat ručně, kdežto když to mám jako kontejner tak je tam vytvářím jako v klasické fomuláři.

Další problém co se mi zatím nepodařilo vyřešit je implementace FormReplikatoru. Mám tady 5 typů elementů které jsou vždy replikovatelné, jen podle nastavení se určí zda tam to tlačítko bude či nikoliv. No zatím jsem to vyřešil jak je vidět výše, klasicky pomocí *addDynamic** a předáním továrničky co provede vložení prvků. Ale víc by se mě líbilo kdybych mohl replikátoru říct z čeho má ten kontejner vytvořit, takže to už zavání úpravou FormReplicatoru.

Samotné hodnoty z těchto elementů nechci získávat na přímo jako u formuláře, ale tak že tomu elementu pošlu entitu co se edituje/vytváří a element sám nastaví hodnotu daného prvku. Mám to tak hlavně proto, protože tyto elementy mají i přemapované hlavní prvky položky (jméno, kategorie, zobrazeni) a také proto, že některé prvky musí udělat i nějaké další akce (např upload obrázku tak aby se provedlo nahrání)

Je tedy správé to řešit pomocí kontejneru či to dělat pomocí kontrolu?

akadlec
Člen | 1326
+
0
-

Takže malé doplnění, pokud by to někoho zajímalo ;) FormReplicator se mě podařilo vyřešit tak, že jsem u něj našel možnost definovat jaká třída se použije na vytvářený kontejner, takže si určím jaký formulářový element se má vytvořit:

// Elements
$this->addContainer('elements');

// Attach all elements
foreach($this->elements as $elementEntity) {
	$elementGroup = $elementEntity->element->group;
	$elementGroup = explode('_', strtolower($elementGroup));
	$elementGroup = implode('', array_map("ucfirst", $elementGroup));

	$elementType = $elementEntity->element->type;
	$elementType = explode('_', strtolower($elementType));
	$elementType = implode('', array_map("ucfirst", $elementType));

	$elementClass = '\\IPub\FormElements\\Elements\\'. $elementGroup .'\\'. $elementType .'FormElement';

	// Check if element class exists
	if ( !class_exists($elementClass) )
		continue;

	$itemEntity = $this->itemEntity;

	// Repeatable element
	if ( $elementEntity->element->group == 'form' && in_array($elementEntity->element->type, array('date', 'email', 'link', 'text', 'textarea')) ) {
		$this['elements']
			->addDynamic($elementEntity->id, function(IFormElement $container) use($elementEntity, $itemEntity) {
				$container
					// Add element to container
					->setElement($elementEntity)
					// Add edited item to container
					->setItem($itemEntity)
					// Create element controls
					->createControls()
					// Create element buttons
					->createButtons();
			}, 1, TRUE)
			// Define element container class for replicator
			->containerClass = $elementClass;

	// Non-repeatable element
	} else {
		//...
	}
}

Ještě bych to teda asi obalil do hlavního kontejneru kterému bych předal jen seznam elementů a on by si to poskladal sám.

Editoval akadlec (14. 1. 2014 23:44)