závislý selectbox – chyba při překreslení snippetu

- admin@easyweb4u.cz
 - Backer | 153
 
Vytvořil jsem závislý selectbox.
Šablona
<?php
                            <form class="ajax" n:name="terapeutsForm">
                                <input n:name="calendarid">
                                <table>
                                    <tr>
                                        <td>
                                            Terapeut:
                                        </td>
                                        <td>
                                            <select id="static" n:name="terapeutid"></select>
                                        </td>
                                    </tr>
                                    <tr>
                                        <td>
                                            Cena:
                                        </td>
                                        <td>
                                            {snippet price}
                                                <select n:name="price"></select>
                                            {/snippet}
                                        </td>
                                    </tr>
                                    <tr>
                                        <td>
                                        </td>
                                        <td>
                                            <input n:name="send">
                                        </td>
                                </table>
                            </form>
?>
script
<script type="text/javascript">
    $('#static').change(function() {
            $.get("?do=selectLoad", {"terapeutid": $(this).val()});
    });
</script>
handler
<?php
    public function handleSelectLoad($terapeutid)
    {
        $form = $this->getComponent('terapeutsForm'); // our form
        $newPrice = [];
        $select = $this->database->table('priceter')->where('terapeutid = ?', $terapeutid)->fetchAll();
        if ($select != null) {
            foreach ($select as $s) {
               $newPrice[$s->price] = $s->nameservice . ' ' . $s->price;
            }
        }
        $form['price']->setItems($newPrice); // set up new values
        $this->redrawControl('price'); // invalidate ajax snippet
    }
?>
a formulář
<?php
namespace App\Forms;
use Nette;
use Nette\Application\UI;
class TerapeutsFormFactory
{
    /** @var Nette\Database\Context */
    private $database;
    public function __construct(Nette\Database\Context $database) {
            $this->database = $database;
    }
    public function create($calendarid)
    {
        $form = new UI\Form;
        $form->addProtection();
        $form->addHidden('calendarid', $calendarid);
        $select = $this->database->table('terapeut')->order('surname')->fetchAll();
        $data = [];
        if ($select != null) {
            $i = 0;
            foreach ($select as $s) {
                $data[$s->id] = $s->surname . ' ' . $s->name;
                if ($i === 0) {
                    $sel = $this->database->table('priceter')->where('terapeutid = ?', $s->id)->fetchAll();
                    if ($sel != null) {
                        foreach ($sel as $e) {
                           $price[$e->price] = $e->nameservice . ' ' . $e->price;
                        }
                    }
                }
                $i++;
            }
        }
        $form->addSelect('terapeutid', 'Terapeut:', $data);
        $form->addSelect('price', 'Cena:', $price);
        $form->addSubmit('send', 'Přidat terapeuta')
                ->setAttribute('class', 'btn btn-success');
        //$form->getElementPrototype()->class('ajax');
        $form->onSuccess[] = [$this, 'processTerapeutsForm'];
        return $form;
    }
    public function processTerapeutsForm($form, $values)
    {
        $insert = $this->database->query('INSERT INTO semiterapeut', [
                'calendarid' => $values->calendarid,
                'terapeutid' => $values->terapeutid,
                'price' => $values->price
        ]);
    }
}
?>
Ono to funguje částečně. Při výběru option se objeví chyba
end() expects parameter 1 to be array, null given
330: $this->global->snippetDriver->leave();
331:
332: }
333:
334:
335: function blockPrice($_args)
336: {
337: extract($_args);
338: $this->global->snippetDriver->enter(„price“,
„static“);
339: ?> <select<?php
340: $_input = end($this->global->formsStack)[„price“];
341: echo $_input->getControlPart()->attributes() ?>><?php echo
$_input->getControl()->getHtml() ?></select>
342: <?php
343: $this->global->snippetDriver->leave();
ale po kliknutí na skip error se závislý select překreslí správnými hodnotami (ale statický select zůstane na 1. – defaultní – hodnotě). Zkusil jsem kolem formuláře dát snippetArea, pak to chybu nehlásí, ale závislý select se nepřepíše.

- admin@easyweb4u.cz
 - Backer | 153
 
MajklNajt napsal(a):
ahoj, aby ti fungoval snippet vo formulári, musíš formulár obaliť do snippetArea, len potom musíš v handleSelectLoad invalidovať aj tú snippetAreu
No to jsem zkusil, chybu to nehlásilo, ale nepřekreslil se ten vnitřní snippet.
Editoval admin@easyweb4u.cz (20. 11. 2018 20:26)

- admin@easyweb4u.cz
 - Backer | 153
 
MajklNajt napsal(a):
Aha, už vidím, v tom JS callbacku by si mal použiť $.nette.ajax() namiesto $.get(), preto sa to snippet neprekreslí
Chyba: Column operator does not accept null argument.
Z toho statického selectu se nyní nepřenesl parameter terapeutid (357: $select = $this->database->table(‚priceter‘)->where(‚terapeutid = ?‘, $terapeutid)->fetchAll();)

- admin@easyweb4u.cz
 - Backer | 153
 
MajklNajt napsal(a):
resp. úplne správne takto:
<script> $.nette.ajax({ type: "GET", url: "?do=selectLoad", data: {"terapeutid": $(this).val()} }); </script>
Díky moc, funguje to :-)

- MajklNajt
 - Člen | 518
 
CZechBoY napsal(a):
@MajklNajt A jeste lip tak abys vse generoval v Nette a ne rucne…
Nemas nahodou ten formular v komponente?
áno, dá sa to aj krajšie, len cez mobil sa mi to nechcelo vypisovať :D ja to mám napr. v takejto konštrukcií:
{include callbackJs, link => "selectLoad", form => "terapeutsForm", input => "terapeutid"}
{define callbackJs}
    <script>
        $("#" + {$control[$form][$input]->htmlId}).off("change").on("change", function () {
            $.nette.ajax({
                type: "GET",
                url: {link {$link}!},
                data: {
                    {$input}: $(this).val(),
                }
            });
        });
    </script>
{/define}
				
- admin@easyweb4u.cz
 - Backer | 153
 
CZechBoY napsal(a):
@MajklNajt A jeste lip tak abys vse generoval v Nette a ne rucne…
Nemas nahodou ten formular v komponente?
No mam. Teď závislý select funguje, ale odesílá se (a uloží) pouze první hodnota (včetně té závislé).
Editoval admin@easyweb4u.cz (21. 11. 2018 9:08)

- admin@easyweb4u.cz
 - Backer | 153
 
admin@easyweb4u.cz napsal(a):
MajklNajt napsal(a):
resp. úplne správne takto:
<script> $.nette.ajax({ type: "GET", url: "?do=selectLoad", data: {"terapeutid": $(this).val()} }); </script>Díky moc, funguje to :-)
Teď závislý select funguje, ale odesílá se (a uloží) pouze první hodnota (včetně té závislé).
Editoval admin@easyweb4u.cz (21. 11. 2018 9:09)

- admin@easyweb4u.cz
 - Backer | 153
 
MajklNajt napsal(a):
@admin@easyweb4u.cz čo máš na mysli pod prvou hodnotou? prvý select alebo prvá value z oboch selectov?
mimochodom, ten kto mi dal mínusku, by mohol aj napísať, čo zlé som poradil, nech sa aj ja poučím…
já jí tam nedal … :-)
No funguje to tak, že když proklikávám první select, správně se mi objevují hodnoty v závislém selectu. Ale hodnoty se odešlou pouze v případě nastavení na první hodnotu prvního selectu (včetně správné zvolené hodnoty závislého selectu). Když se podívám do zdrojového kódu překresleného formuláře, vidím v závislém selectu pouze hodnoty pro první select, i když v prohlížeči vidím vše správně překreslené. Už jsem z toho na palici.
Poznámka: už jsem to tu psal, formulář mám v komponentě.
ještě jsem taky neuvedl tenhle kód v prezenteru
<?php
    protected function createComponentTerapeutsForm()
    {
        $form = $this->terapeutsFormFactory->create($this->id);
        $form->onSuccess[] = function (Form $form) {
            if (!$this->isAjax()) {
                $this->flashMessage('Error');
            } else {
                $this->flashMessage('Přidání terapeuta proběhlo úspěšně.');
                $this->redrawControl('terapeuts');
                $this->redrawControl('flashesAdmin');
            }
        };
        return $form;
    }
?>
					Editoval admin@easyweb4u.cz (21. 11. 2018 10:49)

- CZechBoY
 - Člen | 3608
 
@MajklNajt Je špatně to, že kompletně odebíráš ostatní
onchange handlery na inputu. Dále je potřeba počítat s tím, že form
může být v komponentě a je teda potřeba parametr pojmenovat správně
i s cestou ke komponentě.
Takže správně by to mělo být snad takto:
{include callbackJs, link => "selectLoad", form => "terapeutsForm", input => "terapeutid"}
{define callbackJs}
    <script>
        $("#" + {$control[$form][$input]->htmlId}).on("change", function () {
            $.nette.ajax({
                type: "GET",
                url: {link {$link}!},
                data: {
                    {$control[$form]->getParameterId($input)}: $(this).val(),
                }
            });
        });
    </script>
{/define}
				
- admin@easyweb4u.cz
 - Backer | 153
 
CZechBoY napsal(a):
@MajklNajt Je špatně to, že kompletně odebíráš ostatní onchange handlery na inputu. Dále je potřeba počítat s tím, že form
může být v komponentě a je teda potřeba parametr pojmenovat správně i s cestou ke komponentě.Takže správně by to mělo být snad takto:
{include callbackJs, link => "selectLoad", form => "terapeutsForm", input => "terapeutid"} {define callbackJs} <script> $("#" + {$control[$form][$input]->htmlId}).on("change", function () { $.nette.ajax({ type: "GET", url: {link {$link}!}, data: { {$control[$form]->getParameterId($input)}: $(this).val(), } }); }); </script> {/define}
Hlásí to chybu: Call to undefined method Nette\Application\UI\Form::getParameterId().

- MajklNajt
 - Člen | 518
 
@admin@easyweb4u.cz skús z toho urobiť komponentu týmto spôsobom: https://doc.nette.org/…s/form-reuse#…, kde potom ten handler budeš mať v tej komponente (nie v presenteri, ako to zrejme máš teraz)
@CZechBoY ja som len napísal, ako to používam ja, nie, že je to superuniverzálne a jediné správne riešenie, no zatiaľ som si s tým takto vystačil (aby som dodržal DRY prístup)… samozrejeme, vždy je čo zlepšovať, ďakujem za vysvetlenie :) prekvapilo ma iba mínus bez akéhokoľvek komentára (osobne dávam mínus iba na príspevok, ktorý je úplne off topic alebo je zjavne nesprávny)

- CZechBoY
 - Člen | 3608
 
@MajklNajt on je nesprávný v těch bodech co jsme psal… nenapsal
jsem dřív protože se mi to nechtělo psát na mobilu :D
Nakonec ani ten můj kod není správný…
{include callbackJs, link => "selectLoad", form => "terapeutsForm", input => "terapeutid"}
{define callbackJs}
    <script>
        $("#" + {$control[$form][$input]->htmlId}).on("change", function () {
            $.nette.ajax({
                type: "GET",
                url: {link {$link}!},
                data: {
                    {$control[$form][$input]->lookupPath()}: $(this).val(),
                }
            });
        });
    </script>
{/define}
				
- admin@easyweb4u.cz
 - Backer | 153
 
CZechBoY napsal(a):
@MajklNajt on je nesprávný v těch bodech co jsme psal… nenapsal jsem dřív protože se mi to nechtělo psát na mobilu :D
Nakonec ani ten můj kod není správný…{include callbackJs, link => "selectLoad", form => "terapeutsForm", input => "terapeutid"} {define callbackJs} <script> $("#" + {$control[$form][$input]->htmlId}).on("change", function () { $.nette.ajax({ type: "GET", url: {link {$link}!}, data: { {$control[$form][$input]->lookupPath()}: $(this).val(), } }); }); </script> {/define}
Tak teď se po události change odesílají tyhle parametry:
Therapy:default
id = 31
do = selectLoad
terapeutsForm-terapeutid = 5
Ale handler hodí chybu
Column operator does not accept null argument.
Což značí, že se do něj nepřenese parametr terapeutid resp. terapeutsForm-terapeutid)

- admin@easyweb4u.cz
 - Backer | 153
 
Tak stále na mrtvém bodě. Já jsem nyní vyjmul ten formulář z komponenty a strčil ho do presenteru. Ale nijak jsem si nepomohl. Chová se to úplně stejně.
Pro změnu závislého selektu používám toto – funguje to:
<script type="text/javascript">
    $('#static').change(function() {
        $.nette.ajax({
            type: "GET",
            url: {link selectLoad!},
            data: {"terapeutid": $(this).val()}
        });
    });
</script>
Ale, data a následné uložení odeslaných parametrů se provedou pouze pro první položku statického selectu, a to i když před tím proklikám oba selecty. Pokud nastavím jinou než první položku, ajax se sice provede – volá se správný presenter a id
Therapy:default
id = 31
ale dle mého pozorování se vůbec nevolá funkce, která formulář zpracovává (je též v presenteru).
<?php
    public function processTerapeutsForm($form, $values)
    {
        if (!$this->isAjax()) {
            $this->flashMessage('Error');
        } else {
            $this->flashMessage('TID:' . $values->terapeutid);
            $insert = $this->database->query('INSERT INTO semiterapeut', [
                'calendarid' => $this->id,
                'terapeutid' => $values->terapeutid,
                'price' => $values->price
            ]);
            if ($insert) {
                $this->flashMessage('Přidání terapeuta proběhlo úspěšně.');
                $this->redrawControl('terapeuts');
            } else {
              $this->flashMessage('Chyba! Přenos dat se nezdařil.');
            }
        }
        $this->redrawControl('flashesAdmin');
    }
?>
Pokud závislý select vyřadím, vše funguje jak má.
Věděl by někdo?

- admin@easyweb4u.cz
 - Backer | 153
 
admin@easyweb4u.cz napsal(a):
Tak stále na mrtvém bodě. Já jsem nyní vyjmul ten formulář z komponenty a strčil ho do presenteru. Ale nijak jsem si nepomohl. Chová se to úplně stejně.
Pro změnu závislého selektu používám toto – funguje to:
<script type="text/javascript"> $('#static').change(function() { $.nette.ajax({ type: "GET", url: {link selectLoad!}, data: {"terapeutid": $(this).val()} }); }); </script>Ale, data a následné uložení odeslaných parametrů se provedou pouze pro první položku statického selectu, a to i když před tím proklikám oba selecty. Pokud nastavím jinou než první položku, ajax se sice provede – volá se správný presenter a id
Therapy:default
id = 31ale dle mého pozorování se vůbec nevolá funkce, která formulář zpracovává (je též v presenteru).
<?php public function processTerapeutsForm($form, $values) { if (!$this->isAjax()) { $this->flashMessage('Error'); } else { $this->flashMessage('TID:' . $values->terapeutid); $insert = $this->database->query('INSERT INTO semiterapeut', [ 'calendarid' => $this->id, 'terapeutid' => $values->terapeutid, 'price' => $values->price ]); if ($insert) { $this->flashMessage('Přidání terapeuta proběhlo úspěšně.'); $this->redrawControl('terapeuts'); } else { $this->flashMessage('Chyba! Přenos dat se nezdařil.'); } } $this->redrawControl('flashesAdmin'); } ?>Pokud závislý select vyřadím, vše funguje jak má.
Věděl by někdo?
Kluci, už to neřešte, chyba u mně, natvrdo jsem si k první položce přiřadil závislé položky, měl jsem použít setDefaults()…