Replicator – přesun containeru mezi replikátory

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

Mám formulář s dvěma úrovněmi replikátoru pro editaci položek menu.

Menu → Section → MenuItem

Section a MenuItem jsou contanery definované vlastními třídami. Nad tím vším používám jqueryUI sortables. Při řazení MenuItem uvnitř vlastní sekce se po uložení formuláře uloží položky ve správném pořadí kouzelně bez potřeby cokoli měnit díky toho, jak funguje postupné vytváření komponent při zpracování.

Řeším jak umožnit přesování MenuItem mezi Sections. Tedy přesun Containeru z jednoho replikátoru do jiného replikátoru. Zkoušel jsem si to zjednodušit a využít „magické“ chování replikátoru a při přesunutí do jiné sekce aktivuju v javascriptu pouze tlačítko remove na containeru ze kterého MenuItem přesouvám a všem input v MenuItem změním atribut name, tak aby odpovídal položkám v jiném kontaineru a zároveň měl jiné ID (a pak aktivuji submit formuláře). A spoléhám na to, že si replikátor sám přidá nový kontainer v sekci kde je položka přidána. Funguje to, ale ne spolehlivě.

Napadá někoho jiný postup?

Editoval LeonardoCA (17. 10. 2012 12:48)

Filip Procházka
Moderator | 4668
+
0
-

Jakto, že to nefunguje spolehlivě?

LeonardoCA
Člen | 296
+
0
-

Přidávám si do kódu do každého containeru ID příští položky MenuItem. A to používám pro přesunutý container. Někdy se to ID neincrementuje. Asi tam mám v js kódu nějakou logickou chybu, ale přemýšlím nad tím, jesli je tento postup správný a nebylo by to lepší vyřešit nějak jinak.

Když mi potvrdíš, že takto by to mělo fungovat, tak budu hledat kde se stala chyba :-)

Ještě si nejsem úplně jistý – ID inputů při tomto postupu nehraje žádnou roli, jen odeslané POST data, je to tak?

Btw: Přidal jsem si do replikátoru AddDynamic 2 parametry $containerClass a $buildOptions, abych mohl generovat containery ještě dynamičtěji. https://gist.github.com/3905095 Co na to říkáš?

Editoval LeonardoCA (17. 10. 2012 13:51)

Filip Procházka
Moderator | 4668
+
0
-

$containerClass jsi si nepřidal ;) Leda pokud myslíš parametr konstruktoru. Imho zbytečné.

IDčka položek bych měl ideálně jako jména kontejnerů.

class SectionContainer extends Container
{
	public $buildOptions = array();

	/**
	 * Configure container
	 * @see LeonardoCA\Application\UI.Container::configure()
	 */
	protected function configure()
	{
		if ($this->buildOptions['navigationType']->use_sections) {
			$this->addText('name', 'Section:')
				->setAttribute('class', 'span3')
				->addRule(\Nette\Forms\Form::FILLED, 'Enter %label');
		}

		$menuItems = $this->addDynamic('menuItems', function (MenuItemContainer $item) {
			$item->buildOptions = $item->lookup('BackendModule\Navigation\SectionContainer')->buildOptions;
		}, 1);
		$menuItems->containerClass = 'BackendModule\Navigation\MenuItemContainer';

		$menuItems->addSubmit('add', '+')
			->setAttribute('class', 'btn-small btn-success ajax')
			->setValidationScope(FALSE)
			->addCreateOnClick(TRUE);

		if ($this->buildOptions['navigationType']->use_sections) {
			$this->addSubmit('remove', '-s')
				->setAttribute('class', 'btn-danger ajax')
				->addRemoveOnClick($this->buildOptions['removeSectionCallback']);
		}
	}
}

Ale hledal bych chybu v javascriptu. Pořádně si zkontroluj data, které se posílají na server.

Editoval HosipLan (17. 10. 2012 14:25)

LeonardoCA
Člen | 296
+
0
-

Dík za tipy, ale bohužel asi pracuji s formuláři nestandartně a je to takto pro mne nepoužitelné.

  1. Nastavení třídy containeru až za lookup mi nefunguje, když mám nastaveno $createDefault tak Replicator začne tvořit containery s Nette\Forms\Container.
  2. Lookup nefunguje, protože Replicator připojí container okamžitě ke stromu komponent, v tu chvíli se volá v attached containeru configure() na sestavení a $buildOptions ještě není dostupné, protože tělo továrničky s lookup ještě neproběhlo.
  3. Nelíbí se mi, že by si měl container $buildOptions zjišťovat. $buildOptions budou v mém pojetí dvojího druhu.
  1. definované v konfiguraci – tj. např použit ajax, apod. – ty se budou předávat továrničce přes parametr.
  2. definované na základě dat z databáze nebo od jinud – ty by měl mít IMHO nastaveny od svého parent Containeru až při generování formuláře. A protože parent container je Replikátor tak by to měl umět.

Ale stejně asi začnu psát i pro containery přímo továrničky, ať jsou konfigurovatelné nezávisle přes DI. Tím odpadne potřeba předávat parametr $containerClass replikátoru a předám mu přímo ContainerFactory, ale build options té factory musím předat v constructoru…

V tuto chvíli mám formulář definován takto:

    factories:
        bootstrapRenderer:
            class: Kdyby\Extension\Forms\BootstrapRenderer\BootstrapRenderer(%template%)
            parameters: [template]
        scaffoldingRenderer:
            class: ScaffoldingRenderer
            parameters: [latte]
            setup:
                - setLatte(%latte%)

        form:
            class: BackendModule\BaseForm
            parameters: [template, options = null]
            setup:
                - injectAdminLogger
                - build(%options%)
                - setRenderer(@bootstrapRenderer(%template%))
                #- setRenderer(@scaffoldingRenderer(true))
            tags:
                - form
                - backend
        navigationManagerForm < form:
            class: BackendModule\Navigation\NavigationManagerForm
            setup:
                - injectNavigations

class Manager extends BaseLookoutControl
{
    //...

    protected function createComponentNavigationManagerForm()
    {
        return $this->presenter->context->createNavigationManagerForm(
            $this->createTemplate(), $this->navigationData['type']);
    }
}

Ale abych nemusel vytvářet formulář s použitím context, tak začnu psát formuláře přímo jako továrničky. (Líbí se mi řešení z Venne)

4. Ještě řeším jednu věc. Potřebuji abych jeden replikátor uměl generovat containery s různými třídami. Jediné řešení co mne zatím napadlo (bez nutnosti větších úprav Replikátoru) je definovat si speciální AdapterContainer, jehož jediným účelem by bylo vložit pod sebe libovolný jiný Container, ale zase potřebuje dostat od někud název třídy s jakou má Container vytvářet – tj. opět $buildOptions …

5.

IDčka položek bych měl ideálně jako jména kontejnerů.

Nevidím žádný zvláštní důvod proč. Mi vyhovoje, když si container IDčka generuje sám. Pokud ukládám formulář až jako celek třeba po přidání více položek, tak stejně nevím, které Idčka jsou „pravé“ a které vygenerované… To si budu řešit na úrovni mapperu úplně mimo Formulář a IDčka můžu mít jako input hidden.

Editoval LeonardoCA (18. 10. 2012 21:21)

LeonardoCA
Člen | 296
+
0
-

Už po druhé jsem musel krokovat, abych zjistil příčinu nechtěného chování replikátoru, tak si tu připíšu poznámku.

Pokud má fungovat $createDefault = 1 tak, aby se vytvořil prázdný element jen v případě, že ještě žádný element neexistuje, ale ne v případě kdy replikátor už nějaké kontejnery obsahuje, pak musí být pole hodnot v setValues() pro replikátor indexované od 0.

Zatím mne nenapadlo, jak by se to dalo řešit jinak.

Btw. v dohledné době až trochu zkultivuju js a šablony, zveřejním demo a možná i něco jako GUI formBuilder.

Stay tuned.