Dynamické skrývání prvků ve formuláři

Chcete zobrazit políčka pro zadání adresy pouze, pokud chce uživatel zaslat zboží poštou? Žádný problém :).

Metody addCondition() & toggle()

Klíčem k triku s dynamickým zobrazováním políček je dvojice metod addCondition() & toggle(). Příklad z perexu by s jejich využitím mohl vypadat například takto:

$form->addCheckbox('mail_me')
    ->addCondition($form::EQUAL, true)
        ->toggle();

$form->addText('city');
$form->addText('street');
$form->addText('zipcode');

Metoda addCondition() umí přijímat stejné argumenty jako základní validační metoda addRule(), kromě toho, že nepodporuje žádnou chybovou hlášku. V ukázkovém kódu výše tedy volání doslova znamená „pokud je tento prvek zaškrtnutý“. Metoda toggle() může vždy následovat až po volání addCondition().

Naše ukázka je však zatím nekompletní, metoda toggle() totiž vyžaduje argument: měla by jím být hodnota id atributu ve výsledném HTML kódu – element s tímto IDčkem pak bude dynamicky odhalen/skryt právě v závislosti na podmínce definované v addCondition().

Jak získat ID pro toggle()

Vypadá to, že formulář budeme muset vyrenderovat manuálně, pomocí maker {form}, {label} a {input}, resp. n:name. V šabloně bychom pak mohli zapsat třeba takovýto kód:

<div id="address-container">
    {label city /} {input city}<br>
    {label street /} {input street}<br>
    {label zipcode /} {input zipcode}
</div>

A volání v definice formuláře bychom doplnili následovně:

$form->addCheckbox('mail_me')
    ->addCondition($form::EQUAL, true)
        ->toggle('address-container');

A je to, pokud máme v šabloně přilinkovaný soubor netteForms.js, políčka se budou ihned dynamicky objevovat v závislosti na zaškrtnutí checkboxu.

Jak se vyhnout manuálnímu renderování

Nutnost manuálně vykreslovat formulář může být často velmi otravná – například pokud máte v administraci spoustu velmi podobných formulářů. Pokud používáte DefaultFormRenderer, umí naštěstí framework podat pomocnou ruku. Stačí totiž prvkům nastavit zvláštní option id, která bude ve výsledku vykreslena jako ID atribut elementu obalujícího jak <label>, tak <input>, tedy přesně to, co potřebujeme. V praxi vypadá použití následovně:

$form->addCheckbox('mail_me')
    ->addCondition($form::EQUAL, true)
        ->toggle('address-city')
        ->toggle('address-street')
        ->toggle('address-zipcode');

$form->addText('city')
    ->setOption('id', 'address-city');
$form->addText('street')
    ->setOption('id', 'address-street');
$form->addText('zipcode')
    ->setOption('id', 'address-zipcode');

Proč jsme nemohli všem prvkům nastavit stejnou option id a zjednodušit si tak volání toggle? V HTML správně nesmí být více prvků se stejným ID. Javascript by se pak s takovou situací špatně vyrovnával.

Je také vhodné zmínit, že metoda toggle() má druhý nepovinný parametr, kterým lze obrátit její chování. Pokud bychom zavolali ->toggle('address-city', false), políčko pro zadání města by se naopak zobrazilo pouze tehdy, pokud by checkbox zaškrtnutý nebyl.

Možná jste někde zahlédli metodu setHtmlId, kterou lze libovolnému formulářovému prvku nastavit ID. Ta se však pro naše účely nehodí, protože se nijak nedotýká odpovídajícího <label> – popisek by zůstal viditelný.

Jak přidat animaci

Výchozí implementace toggle() v prohlížeči využívá pouhého nastavení CSS vlastnosti display: none, s čímž žádnou velkou parádu neuděláme. Přidání animace však není problém: stačí v Javascriptu přepsat metodu Nette.toggle vlastním řešením. S využitím jQuery pak třeba můžeme políčka pro adresu nechat přirolovat:

Nette.toggle = function (id, visible) {
    var el = $('#' + id);
    if (visible) {
        el.slideDown();
    } else {
        el.slideUp();
    }
};