DependencySelectBox – Závislé SelectBoxy
- Honza Kuchař
- Člen | 1662
Hezké, do extras s tím! :-) Ještě mě napadla taková věc, když
jsem to řešil, tak ty selectboxy co jsou prázdné – resp. nejde v nich
nic vybrat, jsem skrýval. Odesílací tlačítko bylo jen jedno. Někde se po
tom mrknu, ale teď z hlavy fakt nevím, kde to mám. :-) Ale to moje nebyla to
komponenta, nýbrž bastl. :-) Blbost. Super to máš! ;-)
Editoval Honza Kuchař (17. 5. 2010 21:42)
- Achse
- Člen | 44
Je to parádní záležitost, palec nahoru, mám ale jeden problém – možná je to čistě mou neinformovaností – jak ale použít dependentSelectBox s předvyplněnými údaji.
Budu konkrétní, vybírám zeměpisné údaje: kontinent, zemi, stát/provincii, město. A je mi potřeba použít dependentSelectBox pro úpravu profilu. V registraci, kde se začíná s čistým štítem je to ok, funguje to náramně, ale pro update se mi to nepodařilo nahodit.
Díky za každou radu.
Editoval Achse (10. 6. 2010 12:41)
- Achse
- Člen | 44
4despiq: Díky moc za pomoc, na jednu stranu jsem pěkný trouba, že mi toto nedošlo, na druhou stranu to ale není kompletní řešení, alespoň mně se zdá. Nicméně díky-ti jelikož jsi mne na řešení, nevím zda správné přivedl. Zde ho uvádím pro všechny ostatní, kteří by snad na podobnou obtíž narazili.
<?php
$form->addSelect("location_continent", "Continent:")->setItems(mmarolodex::$continets);
if(!$this->pp->isAjax())
$form['location_continent']->setValue($data['location_continent']);
$form->addDependentSelectBox("location_country", "Country:", $form["location_continent"], array($this, "getValuesSelectLocation_country"));
if($this->pp->isAjax())
$form["location_country"]->addOnSubmitCallback(array($this, "invalidateControl"), "formSnippet");
else
$form['location_country']->setValue($data['location_country']);
$form->addDependentSelectBox("location_state", "State / Province:", $form["location_country"], array($this, "getValuesSelectLocation_state"));
if($this->pp->isAjax())
$form["location_state"]->addOnSubmitCallback(array($this, "invalidateControl"), "formSnippet");
else
$form['location_state']->setValue($data['location_state']);
$form->addDependentSelectBox("location_city", "City / Town:", $form["location_state"], array($this, "getValuesSelectLocation_city"))->addRule(Form::FILLED, "Select your location!");
if($this->pp->isAjax())
$form["location_city"]->addOnSubmitCallback(array($this, "invalidateControl"), "formSnippet");
... pokračování extrémě dlouhého formuláře ...
if(!$this->pp->isAjax())
$form->setDefaults($data);
?>
Možná jsem napsal jen hezký kus ohromné čuňárny ale funguje… :-]
Note: $this->pp
je ukazatel na
defaultPesenter.
Edit: Jo je to čuňárna, jelikož se tím uplně potlačí neajaxová možnost výběru…
Editoval Achse (10. 6. 2010 17:26)
- Foowie
- Člen | 269
dEath napsal(a):
je to super, mně se to moc líbí. a šlo by to v nette udělat pouze pomocí JSON respond, aby se nemusel pořád dokola přijímat nový formulář? Jde to tou invalidací neexistujícího elementu, že?
Možná někdy časem dodělám … ale jestli se přenáší 200b nebo 3kb na změnu pro mě zase není tak primární. .)
- Foowie
- Člen | 269
Dodělány multizávislosti (jeden DependentSelectBox závisí na více komponentách)
Skrátka místo jednoho rodičovského prvku přidejte pole …
$form->addDependentSelectBox("select3", "Výběr 3", array($form["select1"], $form["select2"]), array($this, "getValuesSelect3"));
viz demo …
Přidán experimentálně JsonDependentSelectBox, který posílá v odpovědi jen potřebná data.
Místo $form->addDependentSelectBox
použijte
$form->addJsonDependentSelectBox
a ideálně v metodě
beforeRender
přidejte volání
JsonDependentSelectBox::tryJsonResponse();
. O zbytek je
postaráno ;)
Btw, při tomto druhu odpovědi není nutné invalidovat snippety. Při volání výše zmíněné metody proběhne odeslání odpovědi a ukončí se běh programu. Je zřejmé, že jakékoliv dodatečné změny budou ignorovány (FlashMessages apod ;))
Enjoy
Editoval Foowie (14. 6. 2010 21:21)
- Achse
- Člen | 44
Ani aktuální ani minulá verze, mi nefunguje v IE, nechápu proč, když DEMO v IE šlape.
Z jistých důvodů nemohu použít 1.4.2 Jquery, mám tam 1.3.2, měl jsem
tam problém, že to hlásilo chybu v JS v souboru
jquery.neete.dependentselectbox.js na řákdu
49: $.nette.success(payload);
Succes prý není methodou nette.
Po zapoznámkování celého bloku jsonResponse
to žačalo
běhat pod FF, Chrome, atd.. ale ne v IE.
Podotýkám že mi v IE nechodila i stará verze. V IE se prostě při změně selectBoxu nic nestane.
Nenapadá vás, kde by prosím mohl být problém či záludnost? Popř neznáte nějaký ladicí nástroj do IE, který by mi pomohl při debbugingu?
Děkuji.. :)
Editoval Achse (2. 7. 2010 9:10)
- Foowie
- Člen | 269
jQuery 1.3.2 nepodporuje live.("change"
pro IE :| (řádek 28,
jquery.nette.dependentselectpbox.js). Šlo by to přepsat pomocí knihovny livequery.
Něco jako
initialize: function() {
$.dependentselectbox.hideSubmits();
$('.'+$.dependentselectbox.controlClass).livequery(function() {
$(this).change(function() {
$('#'+($(this).attr('id'))+$.dependentselectbox.buttonSuffix).ajaxSubmit($.dependentselectbox.jsonResponse);
});
});
},
- Achse
- Člen | 44
Foowie napsal(a):
jQuery 1.3.2 nepodporuje
live.("change"
pro IE :| (řádek 28, jquery.nette.dependentselectpbox.js). Šlo by to přepsat pomocí knihovny livequery
…
Děkuji převelice, opravdu velmi – ale velmi jsi mi pomohl.
despiq napsal(a):
urcite bych ale doporucil delat vse pro pouziti nove verze jquery, rychlost jquery se zlepsila velmi znatelne
Jsem si toho vědom, bohužel v projektu jsou nějaké vazby s 1.4.2 nekompatibilní, příší verze projektu dostane celkový update, ale do té doby to musí fungovat, tedy řešení od Foowie je na vyvážení zlatem… :)
Děkuji.
- Achse
- Člen | 44
Mno, a jsem po uši v další potíži. Při tomto způsobu vykreslování, dojde k tomu že se pravděpodobně neprovedou helpery pro dependentselectbox komponenty, a tudíž to nereaguje na událost onChange.
<?php
{widget $form errors}
{widget $form begin}
...
<tr><th>{$form['location_continent']->label}</th><td>{!$form['location_continent']->control}</td></tr>
<tr><th>{$form['location_country']->label}</th><td>{!$form['location_country']->control}</td></tr>
<tr><th>{$form['location_state']->label}</th><td>{!$form['location_state']->control}</td></tr>
<tr><th>{$form['location_city']->label}</th><td>{!$form['location_city']->control}</td></tr>
...
{widget $form end}
?>
Jedná se asi o mou neznalost toho, jak fungují komponenty, ale nevíte někdo – jak z toho ven?
- Foowie
- Člen | 269
Musíš ještě vykreslit
$form['location_continent_submit']->control
$form['location_country_submit']->control
a
$form['location_state']->control
. Toto jsou tlačítka pomocí
kterých se prvek odešle při změně. Případně si ještě v js souboru
uprav funkci hideSubmits
aby se ti skrývaly…
Edit: jinak pro ruční vykreslování formulářů je tady celkem pěkná pomůcka .)
Editoval Foowie (3. 7. 2010 15:33)
- LuKo
- Člen | 116
<?php
use Nette\Application\Presenter;
use Nette\Application\AppForm;
use Nette\Forms\Form;
use DependentSelectBox\DependentSelectBox;
use DependentSelectBox\JsonDependentSelectBox;
use Nette\Forms\FormContainer;
class ComponentPresenter extends BasePresenter {
public function beforeRender() {
JsonDependentSelectBox::tryJsonResponse();
}
public function createComponentForm( $name ) {
return new ComponentFormControl( $this, $name );
}
public function submitForm( $button ) {
$this->hodnoty = $button->form->values;
}
}
class ComponentFormControl extends \Nette\Application\Control {
protected $form;
public function __construct( $presenter, $name ) {
parent::__construct( $presenter, $name );
$this->form = new AppForm();
$this->form->addSelect("select1", "Výběr 1")->setItems(array("/" => "Value = /", "|" => "Value = |", "\\" => "Value = \\"));
$this->form->addJsonDependentSelectBox("select2", "Výběr 2", $this->form["select1"], array($this, "getValuesSelect2"));
if($presenter->isAjax())
$this->form["select2"]->addOnSubmitCallback(array($presenter, "invalidateControl"), "formSnippet");
$this->form->addSubmit("final_submit", "Hodnoty !")
->onClick[] = array($presenter, "submitForm");
}
public function getValuesSelect2( $form ) {
return array(
"A" => $form["select1"]->getValue() . " - A",
"B" => $form["select1"]->getValue() . " - B",
"C" => $form["select1"]->getValue() . " - C",
);
}
public function render() {
$this->form->render();
}
}
?>
Je to osekané demo: http://dependentselectbox.agel-projekt.cz/…t/json-line/.
Zda je metoda getValuesSelect2
v presenteru nebo v komponentě,
nemá na funkci vliv. Pokud v této metodě vypíšu obsah proměnné
$form["select1"]->getValue()
, je tam vždy lomítko „/“.
Navíc je odpověď html stránka, nikoli JSON, přitom
$presenter->isAjax()
vrací TRUE
. Zjišťuji, že
v tom docela plavu :-/
- Foowie
- Člen | 269
Vytvořený AppForm nemáš připojený do stromu komponent ;) Tohle funguje:
class ComponentFormControl extends Control {
public function createComponentForm($name) {
$form = new AppForm($this, $name);
$form->addSelect("select1", "Výběr 1")->setItems(array("/" => "Value = /", "|" => "Value = |", "\\" => "Value = \\"));
$form->addJsonDependentSelectBox("select2", "Výběr 2", $form["select1"], array($this, "getValuesSelect2"));
if($this->getPresenter()->isAjax())
$form["select2"]->addOnSubmitCallback(array($this->getPresenter(), "invalidateControl"), "formSnippet");
$form->addSubmit("final_submit", "Hodnoty !")
->onClick[] = array($this->getPresenter(), "submitForm");
}
public function getValuesSelect2( $form ) {
return array(
"A" => $form["select1"]->getValue() . " - A",
"B" => $form["select1"]->getValue() . " - B",
"C" => $form["select1"]->getValue() . " - C",
);
}
public function render() {
$this->getComponent("form")->render();
}
}
- LuKo
- Člen | 116
Původně jsem ho sice připojoval, ale na špatné místo. Každopádně díky za nakopnutí zase o kus dál v poznávání zákonitostí komponent ;-)
[EDIT] Už vím, proč jsem ho neměl připojený. V Appce mám totiž:
<?php
$this->form = new AppForm();
$this->form->setMethod( AppForm::GET );
?>
Čímž si to opět rozbíjím, protože
$this->form->setParent( $this, $name );
už nezabrání
chybě: The signal receiver component 'form-form' is not found.
Na
tento večer mám o zábavu postaráno ;-)
Editoval LuKo (26. 8. 2010 22:45)
- leninzprahy
- Člen | 150
Jestli jsem to dobře pochopil, pokud chci používat ajaxové obnovení, musím formulář vložit do snippetu?
{snippet mujForm}
{widget mujForm}
{/snippet}
Editoval leninzprahy (10. 9. 2010 18:15)
- Endrju
- Člen | 147
Ahoj, předem – super záležitost tahle komponenta ! Díky!
A teď.. potřeboval bych to trochu upravit, aby to fungovalo následovně:
Mám dva select boxy SelBox1
a SelBox2
, z nichž
ten druhý je závislý na prvním. Tedy SelBox1
do formuláře
přidávám standartní metodou AddSelect()
a SelBox2
addDependentSelectBox()
. Funguje mi to tak jako v demu, ale chtěl
bych, aby když v SelBox1
nebude vybrána hodnota, tak aby
SelBox2
obsahoval nějaká výchozí data.
Jak by se tohle dalo prosím udělat? Díky!
- Ani
- Člen | 226
Je to fajn komponenta, ale mám problém, DependencySelectBox mi funguje, ale když k tomu použiju tohle (pro ajaxové linky), tak DependencySelectBox přestane fungovat (vůbec se nezachití ta změna, v debugbaru se nic neděje). Nevim kde hledat chybu. Každé zvlášť funguje.
<script type="text/javascript">
$("a.ajax").live("click", function (event) {
event.preventDefault();
$.get(this.href);
});
</script>
- Endrju
- Člen | 147
@Foowie: super :).
Jeste jsem zamyslel jednu vec v teto zalezitosti a do ted neprisel na to jak
to vymyslet.
Ve vetsine pripadu mam ve formulari u SelectBoxu nastaveno SkipFirst().
Kdyz se „prodiram“ selectBoxy a nacitaji se hodnoty do nasledujich selBoxu, tak je to jeste v poradku – tzn. vzdy tam mam tu jednu praznou option.
Kdyz si pak vytahnu odeslana data a pomoci nich nastavim vychzi polozky formulare. Napr.:
public function actionDefault($employee = NULL) {
...
$this['filterForm']['id_zamestnanec']->setDefaultValue($employee);
...
}
Tak se sice formular naplni spravnymi hodnotami, ale uz tam neni ta prazna option. Napriklad bych chtel zrusit u jednoho z policek svou volbu a nevybrat nic (pouziti – napr. formular na filtrovani dat). Momentalne to mohu udelat jenom tak, ze znovu vyberu jinou hodnotu nadrazeneho selectBoxu, tim se prekresli podrazeny selectBox a u nej tu hodnotu uz nevyberu.
Snad je to srozumitelne.. ?
Diky moc :) !
Editoval Endrju (16. 11. 2010 23:55)
- Ani
- Člen | 226
Foowie napsal(a):
Ani: Zkoušel jsem to do dema přidat a funguje to normálně. Ani nevím, s čím by to mělo kolidovat… Asi by to chtělo nějakou minimální kostru projektu ve kterém to nefunguje.
Aha, zkoušel jsem to na té ukázce co je u toho. Stačí tam přidat ten script. Teď jsem zjistil, že je to chyba jen (minimálně) v IE8, FF funguje.
Proč to tak je skutečně netušim, nevidim v tom žádnou spojitost.
Editoval Ani (17. 11. 2010 0:04)
- Foowie
- Člen | 269
@Ani: Tak jsem to testoval, a máš pravdu.
V jistých situacích IEčko blbne. Takže máš 2 možnosti. Buď jsem
zjistil, že stačí dát ten kousek JS co jsi posílal až za volání
$.dependentselectbox.initialize();
, nebo použít livequery. Popis
jak s livequery je na předchozí stránce cca uprostřed ;)
BTW: jQuery má u IE8 s .live('change',
nějaké problémy,
píšou to i na jejich
stránkách v komentářích
- Endrju
- Člen | 147
@Foowie: super, odzkoušel jsem. Šlo by ještě, aby
když si natsavíš ve formuláři skipFirst('neco')
respektive
setLeaveFirstEmpty(true)
, aby bylo toto nastaveni respektovano
i když si pak nastavím
setDisabledValue(array('key' => 'value'))
? Když tuto metodu
zavolám, tak na začátku nění žádná prázná hodnota a před-vybere se ta
první hodnota (z těch, které jsem předal v poli)..
No a pak jen takové drobnosti: nebylo by lepší kdyby se řekněme metoda
setLeaveFirstEmpty
jmenovala skipFirst
a v podstatě
přepisovala totu standartní metodu pro slect boxy? Tzn. by měla stejnou či
rozšířenou (ale kompatibilní) funkcionalitou? Vývojáři jsou na to myslím
si zvyklí a taky kvůi zachování nějaké konzistence.. A pak když
nastavuji hodnoty pro SelectBox býva k tomuto volána metoda
setItems(array('key' => 'value'))
, tak by možná bylo lepší,
kdyby se setDisabledValue
nejmenovalo takto, ale třeba
setDisabledItems
, ono ostatně když jsem si poprvé četl název
té metody tady na fóru jak jsi psal, tak mě hned napadlo – a to si můžu
nastavit jen jednu hodnotu? Já bych tam chtěl ale pole (klíč, hodnota), což
jsem si pak z kódu zjistil, že tak vlastně je, takže ok, ale název je
trochu matoucí (pro mne).
Díky :)
Ještě mě teď napadla jedna věc, a to je situace, kdy se nastavuje
„disabledClass“ a kdy ne. Dle mého názoru by bylo správné a logické,
kdyby se CSS třída „disabledClass“ nastavovala závislému selectBoxu
pouze tehdy, pokud v rodičovském prvku není vybrána hodnota (tedy
empty(selected value)
), nikoli však v případě, kdy si
v renderDefault nastavím př.:
$this['filterForm']['dependencySelBox']->setDisabledValue($array);
.
Pak je ještě na zváženou, zda nastavit tuto class taky v případě, když v rodičovském prvku vyberu hodnotu, ale pro tuto hodnotu se do potomka načtou prázdná data. Možná by bylo v tomto případě lepší nastavit druhou class, a to např. „emptyClass“ (výchozí).
Uživatel už si pak může pomocí jQuery třeba sám takovémuto prvku
nastavit diabled (to už bych ponechal čistě na uživateli).
Já to momentálně dělám takto:
@{block filterForm}
{snippet filterFormSnippet}
{control filterForm}
<script type="text/javascript">
<!-- <![CDATA[
$(document).ready(function() {
var val = $('select[name=id_zamestnanec] :last').val();
if (val == "") $('select[name=id_zamestnanec]').attr('disabled', 'disabled');
else $('select[name=id_zamestnanec]').removeAttr('disabled');
});
//]]> -->
</script>
{/snippet}
@{/block}
Tzn. když v rodičovském selectBoxu vyberu hodnotu, ale načte se prázdný seznam (s výchozí prvni prázdnou položkou), tak si zjistím hodnotu poslední option a pokud je prázdná, vím že tento selectBox je taky prázdný a nastavím mu disabled. A jelikož se skript načítá znovu v rámci snippetu, tak se vše správně nastavuje podle toho jaká data získám.
Nicméně podle toho jak navrhuji výše by to bylo čistější a stačilo by pouze vybirat prvek, který má třídu „emptyClass“.
Podobně bych pak mohl manipulovat s prvkem, který by měl třídu „diabledClass“.
A ještě jeden nápad, funkcionalita, kterou jsem postrádal.. Mít možnost nastavit text pro prázdnou hodnotu závislého selBoxu, když se do závislého selBoxu načte prázdný seznam (pole) byť i s přednastavenou první prázdnou položkou.
Něco jako máš DependentSelectBox::$emptyValueTitle = '';
.
Tohle by bylo vhodné přejmenovat např na $skippedFirstValueTitle
a pak by mohla být proměnná pro tuto funkcionalitu pojmenována jako
$emptyItemsTitle
nebo $emptyValuesTitle
.
No, vím, nějak jsem se rozepsal a je toho dost :). Máš tady alepoň pořádný feedback :). Nevím, zda je možné všechno realizovat.. Třeba něco nepůjde z důvodů, která já ze svého úhlu pohledu nevidím, tak pouvažuj a dej vědět .).
Díky :) !
Editoval Endrju (17. 11. 2010 13:28)
- JakubJarabica
- Gold Partner | 184
Formulárová komponenta znova super! Ale mám problém. Mám dva selecty, keď obom nastavím Form::FILLED a mením hodnotu prvého, pri onChange submite dostanem alert, že druhý nie je vyplnený(čož je ofc logické). Ako to fixnem(resp. vypnem pri takomto refreshovaciom submite validáciu celého formu)? Nette mám novšie, príklad mam analogický ako je v LinePresenteri, lenže na validáciu používam netteForms.js(v tvojom príklade som nenašiel data-nette-*) atribúty.
- Oggy
- Člen | 306
mám podobný problém…
hádám, že je to způsobené js kódem, kde se volá při změně selectboxu
ajaxsubmit.. a odešle se formulář ‚odesílácím buttonem‘ nikoli tím
tlačítkem, které vytváří dependentselectbox.
Pokud se neodesílá přes ajax, ale manuálně stiskem tláčítka
vytvořeného dependentselectboxem, tak to funguje – neprobíhá
validace.
Nemá někdo nápad jak js kód upravit, pokud je můj odhad správný.
- fak
- Člen | 48
už to mam
přidal jsem do JS na line 54:
<script>
$.nette.success(payload); //za tohle jsem přidal:
$.dependentselectbox.hideSubmits();
</script>
A ted jsem si všim, že je v jquery.nette.js v zipu příkladu přesně tenhle řádek přidanej – fuj fuj fuj;-)
Editoval fak (13. 12. 2010 14:50)
- Foowie
- Člen | 269
@**fak**: Kdybys četl dokumentaci – sekce JavaScript – závislosti nebo by ses podíval do jquery.nette.dependentselectbox.js tak bys to věděl .)
Editoval Foowie (13. 12. 2010 20:09)
- p3s
- Člen | 8
Ahoj, zacal som pozivat DependentSelectBox, je super, mam ale problem ak chcem pouzit formular na editaciu zaznamu a pouzijem default hodnoty pre formular. Chcem mat hned predvyplene vybrane hodnoty tak pouzivam DependentSelectBox::$disableChilds = FALSE. Hodnota v prvom klasickom selectboxe je ok, hodnota v DependentSelectBoxe je zla, pretoze do metody na ziskanie hodnot sa nepreniesla nastavena default hodnota zo ‚select1‘ ale prva (namiesto „|“' sa preniesla „/“ a tym padom sa v druhom zobrazila nespravna hodnota). Pre nazornu ukazku som upravil demo z prikladu.
class Line2Presenter extends BasePresenter {
public function actionDefault() {
$this["form"]->setDefaults(array("select1" => '|', ));
}
public function getValuesSelect2($form, $dependentSelectBoxName) {
$values = $form->getValues();
Debug::consoleDump($dependentSelectBoxName);
return array(
"A"=>$form["select1"]->getValue()." - A",
"B"=>$form["select1"]->getValue()." - B",
"C"=>$form["select1"]->getValue()." - C",
);
}
public function createComponentForm($name) {
$form = new AppForm($this, $name);
$form->addSelect("select1", "Výběr 1")
->setItems(array("/"=>"Value = /", "|"=>"Value = |", "\\"=>"Value = \\"));
$form->addDependentSelectBox("select2", "Výběr 2", $form["select1"], array($this, "getValuesSelect2"));
if($this->isAjax())
$form["select2"]->addOnSubmitCallback(array($this, "invalidateControl"), "formSnippet");
$form->addSubmit("final_submit", "Hodnoty !")
->onClick[] = array($this, "submitForm");
return $form;
}
public function submitForm($button) {
$this->hodnoty = $button->form->values;
}
}
Viete mi poradit pls co robim zle? Diky.