Vypnutí řazení ve formulářích u addSelect()

Webster.K
Člen | 212
+
0
-

Zdravím všechny, mám takovou zapeklitost se kterou si už nevím rady. Jde o řazení prvků které dávám do formuláře do selectu.

Ve formuláři mám následující:

$form->addSelect('motor', 'Motorizace:')
                ->setHtmlAttribute('data-depends', $model->getHtmlName())
                ->setHtmlAttribute('data-url', $this->link('Car:motory', '#'))
                ->setHtmlAttribute('class', 'form-control form-control-sm')
                ->setPrompt('Nevybráno')
                ->setRequired('Vyberte motorizaci');

A o kus dál pak:

$form->onAnchor[] = fn() =>
                $motor->setItems($model->getValue() ? $this->database->table('motorizace')->select('id, CONCAT(kw,"kw / ",hp,"hp / ",ccm,"ccm") AS motor')->where('model_id', $model->getValue())->order('kw ASC')->fetchPairs('id', 'motor') : []);

O hodně řádků je pak volána action která načítá obsah do tohoto pole a odesílá to jako JSON. Vše voláno přes Ajax. Jde o formulář který má asi 12 závislých prvků které se různě volají a doplňují. To co řeším je to, že z DB se mi vrátí vše seřazeno ve formátu [1=>‚hodnota‘,4=>‚druhá hodnota‘,2=>‚treti hodnota‘]. Což je to čeho potřebuji dosáhnout, ale Nette mi to přehází.
Nemá někdo nápad jak toho docílit, aby Nette neřadilo prvky a nechalo je tak, jak jdou z DB?

Editoval Webster.K (10. 11. 19:21)

Kcko
Člen | 468
+
+3
-

Pokud je to nějaký JSON tak to není chyba Nette, ale vlasnost JS a objektů.

Webster.K
Člen | 212
+
0
-

Ok, tak po čem jít, action ve stejném presenteru vypadá takto:

public function actionMotory($model): void {
        $motor = array('Nevybráno');
        $motor += $this->database->table('motorizace')->select('id, CONCAT(kw,"kw / ",hp,"hp / ",ccm,"ccm"," (",kod_motoru,")") AS motor')->where('model_id', $model)->order('hp ASC')->fetchPairs('id', 'motor');
        $this->sendJson($motor);
    }

a v šabloně pak script, ktery to tam sype

<script>
// najdeme na stránce všechny podřízené selectboxy
    document.querySelectorAll('select[data-depends]').forEach((childSelect) => {
        let parentSelect = childSelect.form[childSelect.dataset.depends]; // nadřízený <select>
        let url = childSelect.dataset.url; // atribut data-url
        let items = JSON.parse(childSelect.dataset.items || 'null'); // atribut data-items

        // když uživatel změní vybranou položku v nadřízeném selectu...
        parentSelect.addEventListener('change', () => {
            // pokud existuje atribut data-items...
            if (items) {
                // nahrajeme rovnou do podřízeného selectboxu nové položky
                updateSelectbox(childSelect, items[parentSelect.value]);
            }

            // pokud existuje atribut data-url...
            if (url) {
                // uděláme AJAXový požadavek na endpoint s vybranou položkou místo placeholderu
                fetch(url.replace(encodeURIComponent('#'), encodeURIComponent(parentSelect.value)))
                        .then((response) => response.json())
                        // a nahrajeme do podřízeného selectboxu nové položky
                        .then((data) => updateSelectbox(childSelect, data));
            }
        });
    });

// přepíše <options> v <select>
    function updateSelectbox(select, items)
    {
        select.innerHTML = ''; // odstraníme vše
        for (let id in items) { // vložíme nové
            let el = document.createElement('option');
            el.setAttribute('value', id);
            el.innerText = items[id];
            select.appendChild(el);
        }
    }
</script>
Kamil Valenta
Člen | 820
+
+2
-

A není to jen tím, že používáš na dvou místech různý order()?
Ono by bylo nejlepší, kdyby to dodávala jedna modelová metoda…

$form->onAnchor[] = fn() =>
    $motor->setItems($model->getValue() ? $this->database->table('motorizace')->select('...')->where(...)->order('kw ASC')->fetchPairs('id', 'motor') : []);
public function actionMotory($model): void {
       $motor = array('Nevybráno');
       $motor += $this->database->table('motorizace')->select('...')->where(...)->order('hp ASC')->fetchPairs('id', 'motor');
       $this->sendJson($motor);
   }
Webster.K
Člen | 212
+
0
-

I když order sjednotím, tak je výsledek stejný. Když tam všude naflákám debugy a koukám, co leze do prohlížeče, tak je to špatně, protože už tady je to právě zpřeházený, má to být 0, 14, 19, 13:

{
    "0": "Nevybráno",
    "13": "47kw / 64hp / 1198ccm (BME, AZQ)",
    "14": "40kw / 54hp / 1198ccm (BMD, AWY)",
    "19": "44kw / 60hp / 1397ccm (AZF, AZE)"
}

Něco to někde řadí, ale do části $this->sendJson($motor) je to správně, odsud to je špatně. Takže to dělá pravděpodobně funkce sendJson

Editoval Webster.K (14. 11. 14:06)

Webster.K
Člen | 212
+
0
-

Tak jsem o trochu dál, momentálně mám tento výsledek:

{
    "0": "Nevybráno",
    "14": "40kw \/ 54hp \/ 1198ccm (BMD, AWY)",
    "19": "44kw \/ 60hp \/ 1397ccm (AZF, AZE)",
    "13": "47kw \/ 64hp \/ 1198ccm (BME, AZQ)"
}

Což je to co potřebuju, ale ve formuláři je to stejně blbe :D obešel jsem to takto: $this->sendRawJson($motor);

private function sendRawJson(array $data): void
    {
        // Nastavíme hlavičku pro JSON odpověď
        $this->getHttpResponse()->setContentType('application/json');

        // Zakódujeme JSON ručně bez zásahu Nette, čímž zajistíme zachování pořadí
        echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

        // Ukončíme skript, aby nedošlo k dalšímu vykonávání
        $this->terminate();
    }

Takže, po čem jít dál? :D když do prohlížeče se to ted přes JSON dává správně. Leda už, že to buď přerovnává prohlížeč, nebo JS

mskocik
Člen | 62
+
0
-

Webster.K napsal(a):

Leda už, že to buď přerovnává prohlížeč, nebo JS

Presne tak, browser to vzdy zoradi podla abecedy. Ved si skus v konzole vypisat: Object.keys({ 1: 2, 5: 1, 2: 5 }). Vrati to ['1', '2', '5'].

Ak potrebujes zachovat poradie, tak to v tom JSONE posli ako pole objektov.

Webster.K
Člen | 212
+
0
-

A to udělám jak? Nějak si nedovedu pod timto nic představit :/

mskocik
Člen | 62
+
0
-

O hodně řádků je pak volána action která načítá obsah do tohoto pole a odesílá to jako JSON.

Ty to posielas ako klasicke asociativne pole. Tak v tej predtym ako volas echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); si tie data skonvertuj na objekty.

$data = array_values(
  \Nette\Utils\Arrays::map($data, fn($value, $key) => ['id' => $key, 'text' => $value])
);

Je dolezite, aby si poslal len tie hodnoty, preto pouzitie array_values. A v JS si tie data pred volanim updateSelectbox(childSelect, data) skonvertuj opat na strukturu ako potrebujes.