Závislé selectboxy: snadná úloha v čistém Nette a JS
- David Grudl
- Nette Core | 8227
Závislé selectboxy jsou velmi snadná úloha na řešení v čistém Nette a JS.
Nejprve si připravím nějaký datový model, který mi bude vracet položky pro hlavní (main) select box a pro podřízený (dependent):
class Model
{
public function getMainItems(): array
{
return ...
}
public function getDependentItems($main): array
{
return ...
}
}
Dále nějaký EndpointPresenter, prostě API, abych ty položky mohl tahat i na straně prohlížeče jako JSON:
class EndpointPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private Model $model,
) {}
public function actionMyData($id): void
{
$items = $this->model->getDependentItems($id);
$this->sendJson($items);
}
}
No a teď udělám formulář se dvěma selectboxy, které provážu.
Položky do podřízeného dávám až v onAnchor
(příp.
onValidate
), tedy ve chvíli, kdy formulář už zná hodnoty
odeslané uživatelem, což v době vytváření formuláře
ještě neví.
class DemoPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private Model $model,
) {}
protected function createComponentForm(): Form
{
$form = new Form;
$main = $form->addSelect('main', 'hlavní:', $this->model->getMainItems())
->setPrompt('----');
$dependent = $form->addSelect('dep', 'podřízený:');
$form->onAnchor[] = fn() =>
$dependent->setItems($main->getValue() ? $this->model->getDependentItems($main->getValue()) : []);
// <-- SEM JESTE NECO DOPLNIM
return $form;
}
}
Tohle by už mělo samo o sobě fungovat (i bez JS), tak, že uživatel vybere první položku, odešle formulář, vybere druhou a znovu odešle.
Doplním JS a AJAX. A tím nejčistějším způsobem, tj. do formuláře
pouze přidám data-
atributy, ve kterých si pošlu do HTML (a
potažmo JS) informaci, které selectboxy jsou provázané (hlavní select bude
mít v atributu data-dependent
název podřízeného) a z jakého
URL získám položky (zapíšu do data-url
), abych mohl následně
provázání vyřešit i v JavaScriptu:
$main
->setHtmlAttribute('data-url', $this->link('Endpoint:myData', '#')) // # is placeholder
->setHtmlAttribute('data-dependent', $dependent->getHtmlName());
return $form;
A zbývá napsat obslužný JS. Dnes se to dá snadno udělat i bez jQuery,
v čistém JS. Následující kód je univerzální, není vázaný na
konkrétní dva selectboxy, ale prováže jakékoliv selectboxy na stránce,
stačí jim jen přidat ony dva data-
attributy.
// najdeme vsechny hlavní selectboxy co maji podrizeny selectbox
document.querySelectorAll('select[data-dependent]').forEach(function (main) {
// a když uživatel změní vybranou položku…
main.addEventListener('change', function () {
let dependent = main.form[main.dataset.dependent]; // podrizeny <select>
let url = main.dataset.url; // URL pro našeptávání
// ...udelame pozadavek na Endpoint presenter a posleme 'id'
fetch(url.replace(encodeURIComponent('#'), encodeURIComponent(main.value)))
.then(response => response.json())
// a nahrajeme do podrizeneho nove data
.then(data => updateSelectbox(dependent, data));
});
});
// vloží nové <options> do <select>
function updateSelectbox(select, items)
{
select.innerHTML = ''; // odstranime vse
for (var id in items) { // vložime nové
var el = document.createElement('option');
el.setAttribute('value', id);
el.innerText = items[id];
select.appendChild(el);
}
}
A je to :)
- kralik
- Člen | 230
Super, už to zkouším.
Jak by toto bylo možné rozšířit o další 2 scénáře použití?
Pomohl by někdo?
- Jeden hlavní select box + dva další selectboxy na něm závislé
- tedy změní se hodnota v hlavním selectboxu a dojde k načtení položek do dvou dalších selectboxů
- 3 select boxy
- 1. hlavní – vybere se hodnota a načtou se data do 2. selectboxu
- 2. vybere se hodnota a dle ní se načtou data do 3. select boxu
- 3. vybere se hodnota
Děkuji :-)
Editoval kralik (12. 1. 2022 7:18)
- Milo
- Nette Core | 1283
Dá se to udělat mnoha způsoby. Například:
add A – do data-dependent neuložíš jednu hodnotu, ale pole hodnot, tedy htmlname každého závislého selectboxu. Následně v actionMyData() pošleš data jako dvouúrovňové pole. A nakonec v JS v change eventu to zase zpracuješ jako pole, tedy o jeden foreach více.
add B – stačí nastavit data atributy i u závislých selectboxů.
- David Grudl
- Nette Core | 8227
Tohle bylo v podstatě napsané z hlavy, nemám to odzkoušené :) Takže bych ještě počkal.
- kralik
- Člen | 230
Milo napsal(a):
Dá se to udělat mnoha způsoby. Například:
add A – do data-dependent neuložíš jednu hodnotu, ale pole hodnot, tedy htmlname každého závislého selectboxu. Následně v actionMyData() pošleš data jako dvouúrovňové pole. A nakonec v JS v change eventu to zase zpracuješ jako pole, tedy o jeden foreach více.
add B – stačí nastavit data atributy i u závislých selectboxů.
mohl bys ukázat přímo v příkladech pro scénáře A i B?
Díky
- Kamil Valenta
- Člen | 820
Proč to neuděláš jak psal @Milo ?
To není pomoc?
Příklad != vypracování implementace pro Tebe.
Ukaž jak jsi to použil a na čem jsi narazil.
- David Grudl
- Nette Core | 8227
Pokud bych měl více nezávislých na jednom hlavním, tak by návrh změnil, a místo aby hlavní odkazoval na jeden podřízený, tak by podřízení odkazovali na jeden hlavní.
PHP:
// místo nastavování atributů na $main by se použil $dependent:
$dependent
->setHtmlAttribute('data-url', $this->link('Endpoint:myData', '#'))
->setHtmlAttribute('data-depends', $main->getHtmlName()); // a odkaz na main
a JS:
// najdeme všechny podřízené selectboxy
document.querySelectorAll('select[data-depends]').forEach(function (dependent) {
let main = dependent.form[dependent.dataset.depends]; // hlavní <select>
// a když uživatel změní vybranou položku…
main.addEventListener('change', function () {
let url = dependent.dataset.url; // URL pro našeptávání
// ...uděláme požadavek na Endpoint presenter a pošleme klíč
fetch(url.replace(encodeURIComponent('#'), encodeURIComponent(main.value)))
.then(response => response.json())
// a nahrajeme do podřízeného nová data
.then(data => updateSelectbox(dependent, data));
});
});
// vloží nové <options> do <select>
function updateSelectbox(select, items)
{
select.innerHTML = ''; // odstraníme vše
for (var id in items) { // vložime nové
var el = document.createElement('option');
el.setAttribute('value', id);
el.innerText = items[id];
select.appendChild(el);
}
}
- David Grudl
- Nette Core | 8227
Pokud by byla kaskáda tří selectboxů, tak prostě přidám třetí prvek
a doplním analogicky data atributy a $onAnchor. V případě způsobu
provázání dle přechozího komentáře s data-depends
by
to bylo:
$s1 = $form->addSelect('s1', null, $this->model->getMainItems())
->setPrompt('----');
$s2 = $form->addSelect('s2')
->setHtmlAttribute('data-depends', $s1->getHtmlName())
->setHtmlAttribute('data-url', $this->link('Endpoint:myData', '#'));
$form->onAnchor[] = fn() =>
$s2->setItems($s1->getValue() ? $this->model->getDependentItems($s1->getValue()) : []);
$s3 = $form->addSelect('s3')
->setHtmlAttribute('data-depends', $s2->getHtmlName())
->setHtmlAttribute('data-url', $this->link('Endpoint:myData', '#')); # jiny odkaz
$form->onAnchor[] = fn() =>
$s3->setItems($s2->getValue() ? $this->model->getDependentItems($s2->getValue()) : []); # jina metoda
Samozřejmě je potřeba do modelu a endpointu doplnit dalsi metody generujici data pro ten třetí prvek.
- Petr Parolek
- Člen | 455
@DavidGrudl trochu OT – bude kod funkční na PHP < 8.0? Jedná
se mi o syntaxy fn
- David Grudl
- Nette Core | 8227
Nepamatuju si od které verze existuje fn. Kde fn není, použije se obdoba s function. Základy programování v PHP tu snad neřešíme.
- David Grudl
- Nette Core | 8227
A nakonec, pokud je málo hodnot a nepotřebuju AJAX, tak není potřeba
vytvářet EndpointPresenter, všechny položky si vyexportuju do
$subItems
a vložím do data atributu data-items
:
$subItems= $this->model->getMainItems();
array_walk($subItems, fn(&$v, $k) => $v = $this->model->getDependentItems($k));
$dependent = $form->addSelect('dep', 'podřízený:')
->setHtmlAttribute('data-items', $subItems)
->setHtmlAttribute('data-depends', $main->getHtmlName());
a pak je načtu přímo:
// a když uživatel změní vybranou položku…
main.addEventListener('change', function () {
// pokud existuje data-items, vložíme položky přímo
if (dependent.dataset.items) {
let items = JSON.parse(dependent.dataset.items);
updateSelectbox(dependent, items[main.value])
return;
}
// ...jinak uděláme požadavek na Endpoint presenter a pošleme klíč
...
});
(Zase vycházím z kodu, kde se vazba dělá přes
data-depends
)
- kralik
- Člen | 230
Ahoj,
zkouším scénář A, jeden hlavní select box a dva selectboxy na něm
závislé.
Presenter
protected function createComponentFormAddDoc(): UI\Form {
$form = new UI\Form();
...
$main = $form->addSelect('zavod', 'Závod', $zavod)
->setHtmlAttribute('class','form-control')
->setDefaultValue($this->mf)
->addRule($form::FILLED,'ZÁVOD musí být vybrán');
$dependent = $form->addSelect('oddeleni', 'Oddělení',$odd)
->setHtmlAttribute('class','form-control')
->setDefaultValue('')
->addRule($form::FILLED,'ODDĚLENÍ musí být vybráno');
$dWf = $form->addSelect('wf', 'Workflow', $wf[$this->mf])
->setHtmlAttribute('class','form-control')
->setDefaultValue('')
->addRule($form::FILLED,'WORKFLOW musí být vybráno');
$form->onAnchor[] = fn () =>
$dependent->setItems($this->mainModel->getDepOddeleni($main->getValue() ?? 'default'));
...
$dependent->setHtmlAttribute('data-url', $this->link('myData','#'))
->setHtmlAttribute('data-depends',$main->getHtmlName());
addobj.latte
// najdeme vsechny hlavní selectboxy co maji podrizeny selectbox
document.querySelectorAll('select[data-depends]').forEach(function (dependent) {
// a když uživatel změní vybranou položku…
let placeholder = encodeURIComponent('#');
let main = dependent.form[dependent.dataset.main]; // hlavní <select>
// a když uživatel změní vybranou položku…
main.addEventListener('change', function () {l}
let url = dependent.dataset.url; // URL pro našeptávání
// ...udelame pozadavek na Endpoint presenter a posleme 'sid'
fetch(url.replace(placeholder, encodeURIComponent(main.value)))
.then(response => response.json())
// a nahrajeme do podrizeneho nove data
.then( data => {
updateSelectbox(dependent, data);
});
{r});
});
// vloží nové <options> do <select>
function updateSelectbox(select, items)
{
select.innerHTML = ''; // odstranime vse
for (var id in items) { // vložime nové
var el = document.createElement('option');
el.setAttribute('value', id);
el.innerText = items[id];
select.appendChild(el);
}
}
- v konzoli dostávám chybu „main is undefined“
- nevím jak do toho zakomponovat i druhý selectbox ($dWf), který je závislý na $main
Předem moc díky za pomoc
- dsar
- Backer | 53
In my opinion having also an example with snippets would promote the Nette-way of doing AJAX stuff (especially for those ones that don't like JavaScript, like me) and furthermore simplify things for newbies (like above).
Even without snippets, since the form works even without JavaScript (a good thing), the code could be reduced by submitting the form and updating the whole form's body
- David Grudl
- Nette Core | 8227
@kralik v JS byla chyba, místo dependent.dataset.main má by dependent.dataset.depends
- kralik
- Člen | 230
David Grudl napsal(a):
A máš tam
$form->onSuccess[] = ...
?
ano, mám toto.
$form->onSuccess[] = [$this, 'submitFormAdddoc'];
Ještě jsem to zkoušel a musel jsem do formuláře přidat fn i pro druhý selectbox.
..
$form->onAnchor[] = fn () =>
$dWf->setItems($this->mainModel->getDepWf($main->getValue() ?? 'default'));
Již se zdá, že to běhá v pořádku.
Jdu ještě vyzkoušet scénář B (se třemi vzájemně provázanými
selectboxy).
Pak jsem kdyžtak dám celé řešení.
Díky
Editoval kralik (19. 1. 2022 12:55)
- kralik
- Člen | 230
Super, funguje to krásně.
Níže shrnuji celé řešení.
Jeden hlavní selectbox a dva závislí
- celkem 3 selectboxy, změnou hodnoty 1. selectboxu dojde k načtení hodnot do dalších dvou selectboxů
PRESENTER
public function actionOptionOddeleni($sid): void{
if($sid){
$data = $this->mainModel->getDepOddeleni($sid);
$this->sendJson($data);
}
}
public function actionOptionWf($sid): void{
if($sid){
$data = $this->mainModel->getDepWf($sid);
$this->sendJson($data);
}
}
protected function createComponentFormAddDoc(): UI\Form {
...
$main = $form->addSelect('zavod', 'Závod', $zavod)
->setHtmlAttribute('class','form-control')
->setDefaultValue($this->mf)
->addRule($form::FILLED,'ZÁVOD musí být vybrán');
$dependent = $form->addSelect('oddeleni', 'Oddělení',$odd)
->setHtmlAttribute('class','form-control')
->setDefaultValue('')
->addRule($form::FILLED,'ODDĚLENÍ musí být vybráno');
$form->onAnchor[] = fn () =>
$dependent->setItems($this->mainModel->getDepOddeleni($main->getValue() ?? 'default'));
$dWf = $form->addSelect('wf', 'Workflow', $wf[$this->mf])
->setHtmlAttribute('class','form-control')
->setDefaultValue('')
->addRule($form::FILLED,'WORKFLOW musí být vybráno');
$form->onAnchor[] = fn () =>
$dWf->setItems($this->mainModel->getDepWf($main->getValue() ?? 'default'));
...
$dependent->setHtmlAttribute('data-url', $this->link('optionOddeleni','#'))
->setHtmlAttribute('data-depends',$main->getHtmlName());
$dWf->setHtmlAttribute('data-url', $this->link('optionWf','#'))
->setHtmlAttribute('data-depends',$main->getHtmlName());
}
LATTE
<script>
// najdeme vsechny hlavní selectboxy co maji podrizeny selectbox
document.querySelectorAll('select[data-depends]').forEach(function (dependent) {
// a když uživatel změní vybranou položku…
let placeholder = encodeURIComponent('#');
let main = dependent.form[dependent.dataset.depends]; // hlavní <select>
// a když uživatel změní vybranou položku…
main.addEventListener('change', function () {l}
let url = dependent.dataset.url; // URL pro našeptávání
// ...udelame pozadavek na Endpoint presenter a posleme 'sid'
fetch(url.replace(placeholder, encodeURIComponent(main.value)))
.then(response => response.json())
// a nahrajeme do podrizeneho nove data
.then( data => {
updateSelectbox(dependent, data);
});
{r});
});
// vloží nové <options> do <select>
function updateSelectbox(select, items)
{
select.innerHTML = ''; // odstranime vse
for (var id in items) { // vložime nové
var el = document.createElement('option');
el.setAttribute('value', id);
el.innerText = items[id];
select.appendChild(el);
}
}
</script>
Tři závislé selectbox
- celkem 3 selectboxy, změnou hodnoty 1. selectboxu dojde k načtení hodnot do 2. selectboxů
- změnou hodnoty ve 2. selectboxu dojde k načtení hodnot do 3. selectboxů
PRESENTER
public function actionOptionLinky($sid): void{
if($sid){
$data = $this->mainModel->getDSlinka($sid);
$this->sendJson($data);
}
}
public function actionOptionPracoviste($lin): void{
if($lin){
$data = $this->mainModel->getDSpracoviste($lin);
$this->sendJson($data);
}
}
protected function createComponentFormAddDoc(): UI\Form {
...
$main = $form->addSelect('sektor', 'Sektor/Oddělení:', $sek)
->setPrompt('Vyberte')
->setHtmlAttribute('class','form-control')
->addRule($form::FILLED,'Sektor/Oddělení musí být vybrán');
$linka = $form->addSelect('linka', 'Linka')
->setHtmlAttribute('class','form-control')
->setPrompt('Nejdříve vyberte sektor/oddělení')
->setHtmlAttribute('data-depends',$main->getHtmlName())
->setHtmlAttribute('data-url', $this->link('optionLinky','#'));
$form->onAnchor[] = fn () =>
$linka->setItems($main->getValue() ? $this->mainModel->getDSlinka($main->getValue()) : []);
$pracoviste = $form->addSelect('pracoviste', 'Pracoviště')
->setHtmlAttribute('class','form-control')
->setPrompt('Nejdříve vyberte sektor/oddělení')
->setHtmlAttribute('data-depends',$linka->getHtmlName())
->setHtmlAttribute('data-url', $this->link('optionPracoviste','#'));
$form->onAnchor[] = fn () =>
$pracoviste->setItems($linka->getValue() ? $this->mainModel->getDSpracoviste($linka->getValue()) : []);
}
LATTE
<script>
// najdeme vsechny hlavní selectboxy co maji podrizeny selectbox
document.querySelectorAll('select[data-depends]').forEach(function (dependent) {
// a když uživatel změní vybranou položku…
let placeholder = encodeURIComponent('#');
let main = dependent.form[dependent.dataset.depends]; // hlavní <select>
// a když uživatel změní vybranou položku…
main.addEventListener('change', function () {l}
let url = dependent.dataset.url; // URL pro našeptávání
// ...udelame pozadavek na Endpoint presenter a posleme 'sid'
fetch(url.replace(placeholder, encodeURIComponent(main.value)))
.then(response => response.json())
// a nahrajeme do podrizeneho nove data
.then( data => {
updateSelectbox(dependent, data);
});
{r});
});
// vloží nové <options> do <select>
function updateSelectbox(select, items)
{
select.innerHTML = ''; // odstranime vse
for (var id in items) { // vložime nové
var el = document.createElement('option');
el.setAttribute('value', id);
el.innerText = items[id];
select.appendChild(el);
}
}
</script>
Děkuji všem za pomoc
- kralik
- Člen | 230
Ahoj,
prosím o radu.
Řeším následující scénář závislých selectboxů:
Ve form mám 4 select boxy ZÁVOD, ODDĚLENÍ, WORKFLOW, PODPIS
Výběrem hodnoty v sb ZÁVOD se načtou položky do ODDĚLENÍ a
WORKFLOW
Výběrem hodoty v sb WORKFLOW se načtou položky do PODPIS
Všechno závislé doplňování funguje.
Ale problém nastane když dojde k submitu formuláře.
Ten vrátí chybu „Please select a valid option“ na sb PODPIS.
Bohužel netuším proč.
Ve formuláři tato hodnota není požadována.
Formulář v presenteru
$form = new UI\Form($this, $name);
$form->setHtmlAttribute('class','ajax');
$main = $form->addSelect('zavod', 'Závod', $zavod)
->setHtmlAttribute('class','form-control')
->setDefaultValue($mf)
->addRule($form::FILLED,'ZÁVOD musí být vybrán');
$dependent = $form->addSelect('oddeleni', 'Oddělení',$odd)
->setHtmlAttribute('class','form-control')
->setDefaultValue($modd)
->addRule($form::FILLED,'ODDĚLENÍ musí být vybráno');
$form->onAnchor[] = fn () =>
$dependent->setItems($this->mainModel->getDepOddeleni($main->getValue() ?? 0));
$dWf = $form->addSelect('wf', 'Workflow', $wf[$mf])
->setHtmlAttribute('class','form-control')
->setDefaultValue(0);
$form->onAnchor[] = fn () =>
$dWf->setItems($this->mainModel->getDepWf($main->getValue() ?? 0));
$dPodpis = $form->addSelect('wfpodpis', 'Podpis',$podpis)
->setHtmlAttribute('class','form-control');
$form->onAnchor[] = fn () =>
$dPodpis->setItems($this->mainModel->getDepPodpis($dWf->getValue() ?? 0));
...
$form->addSubmit('ok', 'Uložit')
->setHtmlAttribute('class','btn btn-primary');
$dependent->setHtmlAttribute('data-url', $this->link('optionOddeleni','#'))
->setHtmlAttribute('data-depends',$main->getHtmlName());
$dWf->setHtmlAttribute('data-url', $this->link('optionWf','#'))
->setHtmlAttribute('data-depends',$main->getHtmlName());
$dPodpis->setHtmlAttribute('data-url', $this->link('optionPodpis','#'))
->setHtmlAttribute('data-depends',$dWf->getHtmlName());
$form->onSuccess[] = [$this, 'submitFormAdddoc'];
return $form;
Latte
<script>
//NOTE: Závislé selectboxy
// najdeme vsechny hlavní selectboxy co maji podrizeny selectbox
document.querySelectorAll('select[data-depends]').forEach(function (dependent) {
// a když uživatel změní vybranou položku…
let placeholder = encodeURIComponent('#');
let main = dependent.form[dependent.dataset.depends]; // hlavní <select>
// a když uživatel změní vybranou položku…
main.addEventListener('change', function () {l}
let url = dependent.dataset.url; // URL pro našeptávání
// ...udelame pozadavek na Endpoint presenter a posleme 'sid'
fetch(url.replace(placeholder, encodeURIComponent(main.value)))
.then(response => response.json())
// a nahrajeme do podrizeneho nove data
.then( data => {
updateSelectbox(dependent, data);
});
{r});
});
// vloží nové <options> do <select>
function updateSelectbox(select, items){
select.innerHTML = ''; // odstranime vse
for (var id in items) { // vložime nové
var el = document.createElement('option');
el.setAttribute('value', id);
el.innerText = items[id];
select.appendChild(el);
}
}
//Konec
</script>
Předem díky
- kralik
- Člen | 230
Pepino napsal(a):
@kralik zkus tomu selectu nastavit
->setRequired(false)
případně ještě
->checkDefaultValue(false)
toto bohužel nepomohlo.
když dám do formuláře
$podpis[1] = 'Někdo';
Zruším tento řádek aby se mi nenačetly závislé hodnoty.
$dPodpis->setHtmlAttribute('data-url', $this->link('optionPodpis','#'))
->setHtmlAttribute('data-depends',$dWf->getHtmlName());
Následně vyberu v selectboxu podpis „Někdo“, tak se formulář submitne a data se předají.
Problém nastane pokud se mají položky selectboxu načíst v závislosti
na vybrané položce „wf“
Při zkoumání prvku jsou tam data Ajaxem správně načtená, do toho
selectboxu „podpis“.
Bohužel netuším kde je problém.
- dehtak
- Člen | 113
a jak ste vyresily problem s timhle ?
Nette\Forms\Controls\SelectBox
public function getValue()
{
return array_key_exists($this->value, $this->items)
? $this->value
: null;
}
Takto zavisly seletbox vraci vzdy null
Vim udelat novou tridu seletbox a do ni napsat novou metodu getValue.
A samozrejme se musi upravit i metoda SetValue.
Ale to by ste do toho vaseho manualu "":https://blog.nette.org/…-javascriptu
na webu meli pridat !!! Protoze jinak je to nefunkcni !!
class ZavislySelectBox extends \Nette\Forms\Controls\SelectBox {
public function setValue($value){
if (empty($this->items) && $value !== null) {
$this->items[$value] = $value;
}
$this->value = $value === null ? null : key([(string) $value => null]);
return $this;
}
public function getValue(){
return $this->value;
}
}
Asi tak nejak zhruba
PS : Misto toho minusovani by ste mohli napsat co tam mam blbe, vsak mam pravdu ze bez novy tridy to nebude fachat
Editoval dehtak (12. 7. 2022 14:36)
- kralik
- Člen | 230
Ahoj,
lze vyřešit „dvojí main závislé selectboxy“?
Narazím když bych chtěl dva nezávislé na sobě, ale závislé selectboxy,
vysvětlím.
1. Závislý main SelectBox
Závod na jehož výběru závisí selectBox Workflow.
→ výběrem Závodu se doplní položky
Workflow = toto funguje dobře.
2. Závislý main SelectBox
Dodavatel na jehož výběru závisí selectBox
Provozovna
→ výběrem 1. selectBoxu Závod se v Dodavateli ani v Provozovně nic
nestane
→ až s výběrem Dodavatel se doplní položky
Provozovna
Bohužel toto nemohu rozchodit.
Zkouším postup https://blog.nette.org/…-javascriptu ten funguje dobře na 1. Závislý SelectBox.
Ale druhý závislý SelectBoxu je bez reakce, ani se nevyvolá Ajax, při změne položky Dodavatel.
Myslím, že problém je v javascriptu, ale to netuším jak vyřešit.
javascript
document.querySelectorAll('select[data-depends]').forEach(function (dependent) {
// a když uživatel změní vybranou položku…
let placeholder = encodeURIComponent('#');
let main = dependent.form[dependent.dataset.depends]; // hlavní <select>
// a když uživatel změní vybranou položku…
main.addEventListener('change', function () {l}
let url = dependent.dataset.url; // URL pro našeptávání
// ...udelame pozadavek na Endpoint presenter a posleme 'sid'
fetch(url.replace(placeholder, encodeURIComponent(main.value)))
.then(response => response.json())
// a nahrajeme do podrizeneho nove data
.then( data => {
updateSelectbox(dependent, data);
});
{r});
});
Můžete mi poradit?
Díky
- Allconius
- Člen | 317
Ahoj, zkoušel jsem také podle postupu zde
Všechno mi funguje, sedí mi i počet položek, ale místo názvů mám všude
[object Object]
Json je ve formátu:
{"id":131,"name":"AAAAAAA"},{"id":21,"name":"Adaptační příprava"}
takže předpokládám že problém je jen v tom JS update:
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);
}
}
jak mu předat ten název z „name“ ?
- Allconius
- Člen | 317
Tak takto je to funkční pro mě (pokud mám všude id a name) :
function updateSelectbox(select, items)
{
select.innerHTML = ''; // odstraníme vše
let el = document.createElement('option');
el.setAttribute('value', 0);
el.innerText = 'Žádná';
select.appendChild(el);
for (i in items)
{
let el = document.createElement('option');
el.setAttribute('value', items[i]["id"]);
el.innerText = items[i]["name"];
select.appendChild(el);
}
}
- vladimir.biro
- Člen | 163
Hello.
Mam malej problem. Zavisle selectboxy mi funguji v poradku, ale na druhej
selectbox (ten zavislej) mam napojene ->toggle() a toto uz nefunguje. Kdyz si
v zavislem selectboxu vyberu tu moznost, ktera zobrazuje dane #id, tak se
zobrazi, to je fajn, ale po zmene halvniho selectboxu, se zavislej selectbox
zmeni v dusledku nactenych novych hodnot, ale dane #id mi zustane zobrazene,
i kdyz dana addCondition() podminka uz neplati. Musim pak prepnout zavislej
selectbox rucne (a pak se pripadne vratit) a az pak #id zmizi.
Resil tohle nekdo pls?
Dekuju za jakekoli rady.
- Pepino
- Člen | 257
@vladimirbiro
Mělo by myslím stačit po změně hlavního selectboxu zavolat
Nette.toggleForm https://github.com/…etteForms.js#L563
Nebo možná nastavit na tom hlavím selectboxu taky toggle.
- Higr
- Člen | 16
Vytvořil jsem si formuláře podle zadání a vše funguje správně. Nicméně pokud formulář použiju pro editování záznamu, dostanu error Value ‚1‘ is out of allowed set [] in field… na
$form->setDefaults();
dělám neco špatně nebo je uvedený tutorial nepoužitelný pro
editování záznamu?
koukal jsem že se to řešilo tady ale asi neúspěšně https://forum.nette.org/…-setdefaults
Editoval Higr (21. 6. 9:59)
- Šaman
- Člen | 2662
Higr napsal(a):
Vytvořil jsem si formuláře podle zadání a vše funguje správně. Nicméně pokud formulář použiju pro editování záznamu, dostanu error Value ‚1‘ is out of allowed set [] in field… na
$form->setDefaults();
dělám neco špatně nebo je uvedený tutorial nepoužitelný pro editování záznamu?
koukal jsem že se to řešilo tady ale asi neúspěšně https://forum.nette.org/…-setdefaults
Od boku: Když nastavuješ defaultní hodnoty, musíš vybrat z těch,
které formulář umožňuje vybrat. Tedy z nastavených dat těch inputů.
Takže v actionEdit musíš nastavit $dependent->setItems()
podle defaultní hodnoty nadřizeného selectu.
Protože v prázdném formuláři ten závislý select nemá žádné možnosti
a ty se mu nastaví pomocí ajaxu až po změně hlavního selectu. Ale ty mu
chceš při editaci nastavit hodnoty ihned, bez toho ajaxového načtení.
- Higr
- Člen | 16
Šaman napsal(a):
Higr napsal(a):
Vytvořil jsem si formuláře podle zadání a vše funguje správně. Nicméně pokud formulář použiju pro editování záznamu, dostanu error Value ‚1‘ is out of allowed set [] in field… na
$form->setDefaults();
dělám neco špatně nebo je uvedený tutorial nepoužitelný pro editování záznamu?
koukal jsem že se to řešilo tady ale asi neúspěšně https://forum.nette.org/…-setdefaultsOd boku: Když nastavuješ defaultní hodnoty, musíš vybrat z těch, které formulář umožňuje vybrat. Tedy z nastavených dat těch inputů. Takže v actionEdit musíš nastavit
$dependent->setItems()
podle defaultní hodnoty nadřizeného selectu.
Protože v prázdném formuláři ten závislý select nemá žádné možnosti a ty se mu nastaví pomocí ajaxu až po změně hlavního selectu. Ale ty mu chceš při editaci nastavit hodnoty ihned, bez toho ajaxového načtení.
Děkuji, takhle mi to funguje. Hodilo by se to dopsat do toho tutorialu. Viděl jsem několik lidí, kteří hledali řešení pro ten samý problém.
- Marek Znojil
- Člen | 90
U prvků, které dědí
Nette\Forms\Controls\ChoiceControl
můžeš metodou
checkDefaultValue(false)
vypnout kontrolu hodnot v seznamu, viz
poslední odstavec https://doc.nette.org/…rms/controls#….