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

admin@easyweb4u.cz
Backer | 143
+
0
-

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.

MajklNajt
Člen | 471
+
+1
-

ahoj, aby ti fungoval snippet vo formulári, musíš formulár obaliť do snippetArea, len potom musíš v handleSelectLoad invalidovať aj tú snippetAreu

admin@easyweb4u.cz
Backer | 143
+
0
-

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)

MajklNajt
Člen | 471
+
0
-

Aha, už vidím, v tom JS callbacku by si mal použiť $.nette.ajax() namiesto $.get(), preto sa to snippet neprekreslí

admin@easyweb4u.cz
Backer | 143
+
0
-

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();)

MajklNajt
Člen | 471
+
0
-

Máš to takto?

<script>
$.nette.ajax({
    url: "?do=selectLoad",
    data: {"terapeutid": $(this).val()}
});
</script>

Editoval MajklNajt (20. 11. 2018 20:59)

MajklNajt
Člen | 471
+
0
-

resp. úplne správne takto:

<script>
$.nette.ajax({
	type: "GET",
	url: "?do=selectLoad",
	data: {"terapeutid": $(this).val()}
});
</script>
CZechBoY
Člen | 3608
+
0
-

@MajklNajt A jeste lip tak abys vse generoval v Nette a ne rucne…

Nemas nahodou ten formular v komponente?

admin@easyweb4u.cz
Backer | 143
+
0
-

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 | 471
+
-1
-

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 | 143
+
0
-

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 | 143
+
0
-

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)

MajklNajt
Člen | 471
+
0
-

@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…

admin@easyweb4u.cz
Backer | 143
+
0
-

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
+
0
-

@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 | 143
+
0
-

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 | 471
+
0
-

@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
+
0
-

@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 | 143
+
0
-

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 | 143
+
0
-

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 | 143
+
0
-

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 = 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?

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()…