Nahrávání RadioListu AJAXove podle vyberu

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
cuga
Člen | 210
+
0
-

Zkusil jsem vyuzit threade „Spojení formulářů s AJAXem“, kde David popisuje, jak na to, ale moc uspesny sem nebyl.

Nevim jak pak dostat druhy RadioList, ktery se nacte na zaklade prvniho vybraneho.

V presenteru mam

$form->addGroup('Způsob dodání');
$form->addRadioList('shipping','', $shipping->getShipping()->select('*')->fetchPairs('id','title'));

$form->addGroup('Způsob platby');
$form->addRadioList('payment','');

$form['shipping']->getControlPrototype()
  ->onchange('loadBox(1, this.value);');
$form['payment']->getControlPrototype()
  ->onchange('loadBox(2, this.value);');

	...

if ($form->isSubmitted()) {
  // teprve nyni dosadime hodnoty
  $form['payment']->setItems( ... tady netusim co hodit ...);

  if ($form->isValid()) {}

} else {
  $form['payment']->setDisabled();
  $form['submit1']->setDisabled();
}

v sablone mam

<script type="text/javascript">

function loadBox(phase, value)
{
  if (phase === 1) {
    // zvolena hodnota prvniho select boxu
    $('#frmform-submit1').attr('disabled', true);

    // natáhne obsah druhého selectboxu pomocí AJAXu
    $.get("?do=loadData", {"value": value, "phase": phase}, function(data) {
            $('#frmform-payment').parent().html(data);
    });

  } else if (phase === 2) {
    $('#frmform-submit1').attr('disabled', false);
  }
}

</script>

{$form}

a pak jeste handler

public function handleLoadData($phase, $value) {
  $form = $this->template->form;

  $payment = new Payments();

  if ($phase == 1) {
          // naplníme select box prvky a vypíšeme na výstup
          $form['payment']->setItems($payment->getPayments()->select(array('id','title'))->where('shippingId = %i',$value)->fetchPairs('id','title'));
          $form['payment']->setDisabled(FALSE);
          echo $form['payment']->getControl();
  }

  // konec zpracování
  $this->terminate();
}

handler mi vrati spravne to co ma, ale uz nevim jak to dostat zpatky do toho formulare…

DOPLNENO: a jeste by me zajimalo, jak docasne do te druhe skupiny napsat „Vyberte způsob platby“. Jakmile by uzivatel vybral způsob, text by se vyhodil a misto nej by byl pozadovany RadioList… diky

Editoval cuga (16. 5. 2009 16:13)

cuga
Člen | 210
+
0
-

co, nenajde se nikdo kdo by me posoupnul trosku dal???

nAS
Člen | 277
+
0
-

Podívej se třeba na tento příklad. Jde o to, že bys měl vyrenderovat rovnou vše a nepotřebné věci skrýt. A poté po kliknutí pouze zobrazit konkrétní část a ne načítat AJAXem. Takhle ti aplikace bude fungovat (i když méně komfortně) i bez JavaScriptu a vyhneš se časovému zdržení při AJAXovém požadavku.

cuga
Člen | 210
+
0
-

konecne se nekdo ozval :) kouknu, diky moc…

cuga
Člen | 210
+
0
-

to nAS:
hmmm, krome toho, ze se mi to nepodarilo rozchodit, je taky problem, ze nevim, jak vytahnout z jednoho radiolistu aktualni hodnotu a podle toho odkryt pozadovany div…

zkusil sem to takhle:

$form->addGroup('Způsob dodání');
$sub = $form->addContainer('shipping');
foreach($shipping->getShipping()->select('*')->fetchPairs('id','title') as $id => $title) {
$sub->addRadioList('shipping'.$id,'', array($id => $title))
  ->addRule(Form::FILLED, 'Vyberte způsob dodání')
  ->addCondition(Form::FILLED, TRUE) // conditional rule: if is checkbox checked...
    ->toggle('paymentBox'.$id); // toggle div #sendBox

}

$form->addGroup('Způsob platby');
$sub = $form->addContainer('payment');
foreach($payments->getPayments()->select('shippingId')->toFluent()->setFlag('DISTINCT')->fetchAll() as $row) {
  $form->addGroup()
    ->setOption('container', Html::el('div')->id('paymentBox'.$row->shippingId));
  $sub->addRadioList('payment'.$row->shippingId,'', $payments->getPayments()->select('*')->where('shippingId = %i',$row->shippingId)->fetchPairs('id','title'))
    ->addRule(Form::FILLED, 'Vyberte způsob platby');
}

Aaaakorat, ze diky tomu mam vic radiolistu, takze muzu vybrat zaroven dva druhy dopravy i dva zpusoby platby…

nAS
Člen | 277
+
0
-

Třeba takhle:

// Shipping type
$shipping = $form->addRadioList('shipping', 'Shipping type', array('plane' => 'by plane', 'bus' => 'by bus'));
$shipping->addCondition(Form::EQUAL, 'plane')
		->toggle('planeBox');
$shipping->addCondition(Form::EQUAL, 'bus')
		->toggle('busBox');

// group Plane
$form->addGroup()
	->setOption('container', Html::el('div')->id('planeBox'));
$form->addText('planeNum', 'Plane Number', 35);

// group Bus
$form->addGroup()
	->setOption('container', Html::el('div')->id('busBox'));
$form->addText('busNum', 'Bus Number', 35);
cuga
Člen | 210
+
0
-

Diiiiiiky moc :) facha to…

phx
Člen | 651
+
0
-

Vcera jsem ten ajax resil taky a problem je v tom, ze to co pouzivas posila HTML fragment NEobaleny v JSON. Pokud jsi jeste nacetl neco takovehoto tak vsechny prichozi pozadavky v AJAX jsou ocekavany v JSON coz ty nemas. Takze to nenacti a je to vyresene:)

Vcera mi to trvalo asi hodinu nez jsme to vyresil:)

Asi by to chtelo aktualizovat onen https://forum.nette.org/…aru-s-ajaxem nebo dovysvetlit problem s JSON. Ja na to nemam znalosti – zaitm.

dotTwelve
Člen | 167
+
0
-

Tak nakonec jsem to rozpohyboval (pozadavek totiz skoncil pri volani neexistujici funkce spinner() – casem dopisu), ale nenacitaji se mi hodnoty do druheho selectboxu:

Mam to takto:

<?php
if ($form->isSubmitted())
            {
                // teprve nyni dosadime hodnoty
                $form['mesto']->setItems( ... );

                if ($form->isValid()) {

                }
            } else {
                $form['mesto']->setDisabled();
                $form['submit1']->setDisabled();
            }
?>

Tady nevim co mam dosadit za hodnoty i kdyz dam napr $form[‚mesto‘]->setItems(array(1 ⇒ ‚hodnota1‘)); tak se mi toto pole nenacte.

Dale mam funkci loadBox()

<script type="text/javascript">
function loadBox(phase, value)
{
        if (phase === 1) {
                // zvolena hodnota prvniho select boxu
                $('#frmregistraceForm-submit1').attr('disabled', true);

                // zobrazi symbol, že se něco načítá
                //spinner();

                // natáhne obsah druheho selectboxu pomocí AJAXu
                $.get("?do=loadData", {"value": value, "phase": phase}, function(data) {
                        $('#frmregistraceForm-mesto').parent().html(data);
                });

        } else if (phase === 2) {
                // zvolena hodnota druheho select boxu, povolí tlačítko submit
                $('#frmregistraceForm-submit1').attr('disabled', false);
        }
}
</script>

handler je takovyto:

<?php
public function handleLoadData($phase, $value)
    {
        $form = $this->template->form;
        $data = new Database();

        if ($phase == 1) {
                // naplníme select box prvky a vypíšeme na výstup
                $form['mesto']
                    ->setItems($data->getMesta()->select(array('id', 'nazev'))->where('kraj = %s', $value)->orderBy('nazev')->fetchPairs('id', 'nazev'));
                $form['mesto']->setDisabled(FALSE);
                echo $form['mesto']->getControl();
        }

        // konec zpracování
        $this->terminate();
    }
?>

Editoval dotTwelve (24. 6. 2009 18:12)

dotTwelve
Člen | 167
+
0
-

Druhy SELECTbox se mi v pozadi nacte, s tim ze:

<select onfocus="this.onmousewheel=function(){return false}" onchange="loadBox(2, this.value);" name="mesto" id="frmregistraceForm-mesto"><option value="646">ADAMOV</option>
atd...

Jenze po vybrani kraje v prvnim selectu se skryje druhy select a uz se nezobrazi…

Kde muze byt chyba? :-(

Ztroskota mi to na funkci echo:

<?php
echo $form['mesto']->getControl();
?>

Editoval dotTwelve (26. 6. 2009 12:32)

dotTwelve
Člen | 167
+
0
-
Cannot send header after HTTP headers have been sent (output started at app/presenters/InstitutionPresenter.php:244).

To je presne na radku, kde mam:

<?php
echo $form['mesto']->getControl();
?>

Editoval dotTwelve (30. 6. 2009 9:20)

dotTwelve
Člen | 167
+
0
-

Po delsi pauze se opet ptam, zda-li je nekdo ochoten pomoci…

Editoval dotTwelve (28. 7. 2009 9:45)

cuga
Člen | 210
+
0
-

a opravdu ti nefunguje ten postup, ktery je popsany v Spojeni formulare s AJAXem??? I pres upozorneni, ktere tu hodil PHX??? Protoze mi prijde, ze resi presne tvuj problem…

dotTwelve
Člen | 167
+
0
-

No jenze ja nechapu to upozorneni, jestli mas na mysli JSON data.

cuga
Člen | 210
+
0
-

aha :) no problem je v tom, ze kdyz pouzijes ten jquery kod, tak ti Handler vraci vysledky v JSONu, ale s tim navod v tom druhe threadu nepocita, takze to nebude fungovat…

kdyz ho nepouzijes (tzn. nedas do <head> tag <script … /> tak ti to bude vracet HTML a melo by to byt v cajku…

dotTwelve
Člen | 167
+
0
-

problem je v tom, ze jelikoz pouzivam i datagrid, tak mam v hlavicce spoustu tagu <script>

<!-- jQuery -->
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript" src="http://jqueryui.com/latest/ui/ui.core.js"></script>
<script type="text/javascript" src="http://jqueryui.com/latest/ui/ui.datepicker.js"></script>
<script type="text/javascript" src="/0.1/document_root/js/jquery.livequery.js"></script>
<!-- custom JS -->
<script src="/0.1/document_root/js/jquery.nette.js" type="text/javascript"></script>
<script src="/0.1/document_root/js/datagrid.js" type="text/javascript"></script>
<script src="/0.1/document_root/js/form.js" type="text/javascript"></script>

tak a ted kterej vyhodit?

cuga
Člen | 210
+
0
-

dle meho

<script src=„/0.1/document_root/js/jquery.nette.js“ type=„text/javascript“></script>

ale nevim jak ti to ovlivni ten datagrid… pripadne pres {ifCurrent … } ho na te konkretni strance vyhod…

dotTwelve
Člen | 167
+
0
-

Tak uz mi to funguje, akorat problem nastava pri vyberu jine, nez prvni polozky v prvnim select boxu.
Prvni polozka posila:

Date		Wed, 29 Jul 2009 12:23:24 GMT
Server		Apache
X-Powered-By	Nette Framework
Expires	Mon, 23 Jan 1978 10:00:00 GMT
Cache-Control	s-maxage=0, max-age=0, must-revalidate
Pragma	no-cache
Set-Cookie	PHPSESSID=928b10a40fded10dfee37d4921d4b5384b9e735c; path=/; httponly
nette-browser=0.068650825888999; path=/; httponly
Content-Length	222
Keep-Alive	timeout=15, max=96
Connection	Keep-Alive
Content-Type	application/json

zatimco ostatni polozky ze select boxu posilaji hlavicku

Date	Wed, 29 Jul 2009 12:25:20 GMT
Server	Apache
X-Powered-By	Nette Framework
Expires	Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control	no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma	no-cache
Set-Cookie	PHPSESSID=928b10a40fded10dfee37d4921d4b5384b9e735c; path=/; httponly
nette-browser=0.068650825888999; path=/; httponly
Vary	Accept-Encoding
Content-Encoding	gzip
Content-Length	12561
Keep-Alive	timeout=15, max=100
Connection	Keep-Alive
Content-Type	text/html

Vidim zde problem v Content-Type. Mate nejaky napad jak rici vsem polozkam pri vyberu v select boxu aby posilaly content-type: application/json?
Respektive to posila znova hlavicku, coz by asi nemelo…

Editoval dotTwelve (29. 7. 2009 16:46)

MartinJanda
Člen | 60
+
0
-

Tak Content-Type v hlavičce odezvy handleru změníš takto:

$response = Environment::getHttpResponse();
$response->setHeader('Content-type', 'application/json');
dotTwelve
Člen | 167
+
0
-

no takze nechapu proc se mi posila znova hlavicka viz.

Cannot send header after HTTP headers have been sent (output started at /0.1/app/presenters/InstitutionPresenter.php:286).

to je presne tento radek

echo $form['mesto']->getControl();
MartinJanda
Člen | 60
+
0
-

Problém má 2 řešení, buď pro danou stránku vynecháš:
<script src=„/0.1/document_root/js/jquery.nette.js“ type=„text/javascript“></script>

nebo tu komunikaci, resp. odpověď z handleru předěláš na json řetězec a potřebně upravíš.

Bohužel tomu rozumím dost málo, abych ti konkrétněji pomohl, ale třeba se někdo najde.


Ten ti vypíše ten HTML kód pro formulář.

Pořadí by mělo být:

  1. hlavička
  2. obsah
  3. v handleru musíš mít $this->terminate();

Ještě se může stát, že z handleru ti to vrací chybu, tedy celou stránku s Debug výstupem, kde se hlavička už posílá. Nevím jestli používáš Firebug, ale tam by to bylo vidět.

Editoval romansklenar (30. 7. 2009 11:36)

romansklenar
Člen | 655
+
0
-

Pokud máš v InstitutionPresenter.php na řádku 286 echo $form['mesto']->getControl(); tak čemu se potom divíš? Nemá to tam co dělat. Ta hlavička se neposílá „znova“, tam kde ti to řve by se měla hlavička odesílat poprvé, jenže tím echem už jsi poslal něco na výstup…

EDIT: to už měsíc řešíš takovouto chybu?

dotTwelve
Člen | 167
+
0
-

No i kdyz vynecham ten script jquery.nette.js tak to zlobi.
Jj mel jsem mezi tim prestavku, pak jsem myslel ze se na to vybodnu ale rikam si, ze mozna uz jsou dobri lide po dovoleny a radi pomohou ;)
Tvoje reseni Romane?

romansklenar
Člen | 655
+
0
-

Vykreslovat formulář v šabloně, tam kde patří – echo v presenteru nemá co dělat.

MartinJanda
Člen | 60
+
0
-

A v jaké situaci vlastně ty selectBoxy chceš použít? Nebude vhodnější použít jeden selectBox s větší hloubkou?
Jako že do addSelect nacpeš:

array(
	'test' => 'test',
	'test1' => 'test1',
	'podtest' => array(
		'test2' => 'test2',
		'test3' => 'test3'
)
)

Pokud těch položek není stovka, tak to bude fungovat, bude to přístupnější a v konečném důsledku použitelnější, ne?

cuga
Člen | 210
+
0
-

echo v presenteru mozna nema co delat, ale priklad vychazi z toho od Davida, kde to takhle je, takze by mozna stalo za to upravit puvodni tak at spolupracuje s jQuery pluginem…

dotTwelve
Člen | 167
+
0
-

Prave ze je tam toho dost, jedna se totiz nejdriv o vyber kraje, pak vyber mesta, a pokud existuji mestske casti tak jeste treti select.

Cuga – presne tak, vzal jsem to z prikladu, kde to takto je…rad bych uvital moznost dodelani pro jQuery.

Nasel by se nejaky dobrak, co by byl ochoten vysvetlit problem s jQuery, popripade upravil priklad od Davida?

Editoval dotTwelve (31. 7. 2009 14:27)

cuga
Člen | 210
+
0
-

mam s timhle drobny problem… kdyz zmenim AJAXove obsah formulare, tak se mi pri odeslani neulozi a v zmenenych polich je NULL… resil to nekdo???

Ola
Člen | 385
+
0
-

To je schválně, aby nešlo měnit hodnoty například v selectu zvenčí přes javascript nebo přes úpravu zdrojového kódu např. v opeře.

cuga
Člen | 210
+
0
-

no to je sice super, ale jak to mam osefovat?

Panda
Člen | 569
+
0
-

Máš 3 varianty:

  1. zajistit, aby měl formulář po odeslání stejnou definici, jako před změnou AJAXem – toho můžeš docílit pomocí dodatečných parametrů
  2. podědit třídu SelectBox a nastavit pole $allowed po nastavení položek v poli na všechny možné hodnoty
  3. $form['select']->getRawValue() a ověřovat ručně
cuga
Člen | 210
+
0
-

co myslis tou stejnou definici?

zkousel jsem i ten rawValue, ale moc se nechytam, vrati mi to kompletni SelectBox, ale ve value je porad ulozena puvodni hodnota, i kdyz v Items i Allowed jsou nove nahrane hodnoty.

Ola
Člen | 385
+
0
-

Zrovna před týdnem jsem začal pracovat na komponentě AjaxSelectBox, která umožňuje propojení s normálním SelectBoxem, s vypuštěním jsem čekal na úpravu v Nette, pokud to nepůjde, budu ji muset vydat „zaprasenou“. Má to ale pár mušek:

  • musí se to renderovat ručně, nebo se zobrazí jen label
  • musíš tomu předat dost speciální pole položek (prostě pro každou hodnotu z „nadřazenýho selectu“ musíš přidat alespoň jednu podřazenou položku), které může být dost velké (plánuju napojení na DibiDataSource, zatím jsem to ale s dibi nechtěl moc svazovat)
  • je to dost svázané s AJAXem, bez něj se nezobrazí ovládací prvek a je třeba formulář odeslat ručně (pak se již prvek zobrazí)
  • mohou blbnout chybové hlášky

AjaxSelectBox

Editoval Ola (7. 3. 2010 8:56)

Panda
Člen | 569
+
0
-

Ten getRawValue() musíš volat až ve zpracování formuláře – data ze selectboxu nebudeš tahat z $form->values, ale z $form['select']->getRawValue().

A ta stejná definice se bez konkrétního příkladu docela špatně vysvětluje. Když zobrazuješ formulář, tak si ho nadefinuješ s nějakými hodnotami a vytvoříš si komponentu. Když ten formulář odesíláš, tak se Ti ta komponenta vytváří znovu. Pokud jsi ale mezi vykreslením formuláře a jeho odesláním formulář změnil pomocí AJAXu, tak už Ti pak definice odeslaného formuláře nemusí korespondovat s tou, která se Ti vytvoří při jeho odeslání. A Ty by jsi právě měl zajistit, aby byly shodné.

Doufám, že je tomu alespoň trochu rozumět.

cuga
Člen | 210
+
0
-

ukazu ti jak to mam a co mi nefunguje :)

$form->addSelect('shipping', 'Doprava', $shippingsArray)
    ->setDefaultValue($shipping)
    ->controlPrototype->class('select');
...
$form->addGroup('Dodací údaje');
    $sub = $form->addContainer('delivery');
    ...
    $sub->addSelect('country', 'Stát', $countries)
        ->controlPrototype->class('select');
    $sub['country']->getControlPrototype()
        ->onchange('loadBox(this.value);');

potom handler

public function handleLoadData($part, $value)
{
    $form = $this->template->form;

    if($part == 'shipping') {
        ...
        $form['shipping']->setItems($shippingsArray);
        echo $form['shipping']->getControl();
    } else if($part == 'payment') {
        ...
    }

    // konec zpracování
    $this->terminate();
}

a zpracovani formulare

$shipPrice = $this->shippingsModel->getShippingPrice($form['shippingLimit']->value,$form['shipping']->value,$form['delivery']['country']->value,$cart->cartSum);

a pokud nekdo zmeni stat, tak je v $form[‚shipping‘]->value NULL dle ocekavani… pokud dam $form[‚shipping‘]->getRawValue()->value, tak si taky nepomuzu, protoze mi vraci to puvodne vybrane ID

cuga
Člen | 210
+
0
-

opet se pripominam, jestli me nekdo muze nasmerovat o kousek dal, diky…

Editoval cuga (9. 3. 2010 15:30)

cuga
Člen | 210
+
0
-

tak nakonec jsem to poresil pres to getRawValue, ale pri finalnim testovani v IE6–8 jsem narazil na hodne velkou neprijemnost, kdyz zmenit stat, tak se mi misto zmenenych selectu vypise

{"redirect":"http:\/\/localhost\/div-shop\/kosik\/"}

pritom JSON data by vracet vubec nemel. resili jste to nekdo? zadny nette jquery plugin nalinkovany nemam…

pokud nekomu pomuze, tak hlavicka vracena FF

object(RedirectingResponse)#55 (2) { ["uri":"RedirectingResponse":private]=>  string(83) "http://localhost/div-shop/kosik/dodaci-udaje/?do=loadData%3Fpart%3Dshipping&value=2" ["code":"RedirectingResponse":private]=>  int(301) }

a IE vraci

object(RedirectingResponse)#64 (2) { ["uri":"RedirectingResponse":private]=> string(32) "http://localhost/div-shop/kosik/" ["code":"RedirectingResponse":private]=> int(302) }

Editoval cuga (17. 3. 2010 15:51)

cuga
Člen | 210
+
0
-

tak jeste zmena, uz jsem zjistil proc hazel redirect. duvod je ten, ze mi AJAX v IE „maze“ session s kosikem. dava to nejaky smysl?

cuga
Člen | 210
+
0
-

tak mohlo za to v session $verificationKeyGenerator…

mlha
Člen | 58
+
0
-

Panda napsal(a):

A ta stejná definice se bez konkrétního příkladu docela špatně vysvětluje. Když zobrazuješ formulář, tak si ho nadefinuješ s nějakými hodnotami a vytvoříš si komponentu. Když ten formulář odesíláš, tak se Ti ta komponenta vytváří znovu. Pokud jsi ale mezi vykreslením formuláře a jeho odesláním formulář změnil pomocí AJAXu, tak už Ti pak definice odeslaného formuláře nemusí korespondovat s tou, která se Ti vytvoří při jeho odeslání. A Ty by jsi právě měl zajistit, aby byly shodné.

Formulář se mi zobrazuje po přijetí signálu s parametry. Jak zajistím, aby se tyto parametry uchovaly v componentě formuláře. Formulář je „obecná komponenta“ – nemůžu použít persistentní parametry definované na úrovni třídy. Existuje něco jako „dynamicky definované persistentní parametry“ componenty?

mlha
Člen | 58
+
0
-

Toto jsem už vyřešil. Ne přes persistentní parametry, ale přes hidden pole. Zásadní je uvědomit si, jak se komponenta formuláře chová při jeho odeslání.

Vždy se volá nejprve createComponent!!!

Při signálu s parametrem id není tento parametr po odeslání formuláře již dostupný, takže správný postup:

<?php
$form = new AppForm($this, 'form'); //v tento okamžik má formulář k dispozici i parametry signálu
$id = $form->getParam('id'); //při odeslání formuláře není dostupný
$input = $form->addHidden('id');
if (is_null($id)) $id = $input->getValue(); //při odeslání formuláře nahrazuje parametr hidden
$input->setValue($id);
...
?>