Bug v selectboxu ->setPrompt() nelze pouzit v nette 2.4

krucinal
Člen | 38
+
0
-

V novem nette (nevim kdy to vzniklo, delal jsem upgrade z 2.0 na 2.4) mi na selectboxech prestalo fungovat setPrompt().
Jakmile se ho pokusim pouzit, po odeslani formulare skoncim s vyjimkou, ze hodnota neni v povolenem rozsahu, coz je pochopitelne nesmysl, jelikoz tento prompt byl pridan samotnou definici formulare.

Stejnou chybu vyhazuje i vzorovy kod z dokumentace na strankach nette.

<?php
$countries = [
    'Europe' => [
        'CZ' => 'Česká Republika',
        'SK' => 'Slovensko',
        'GB' => 'Velká Británie',
    ],
    'CA' => 'Kanada',
    'US' => 'USA',
    '?'  => 'jiná',
];

$form->addSelect('country', 'Země:', $countries)
    ->setPrompt('Zvolte zemi');
?>

Dotaz zni, zmenila se nejak logika a mam setPrompt prestat pouzivat a vyrobit si vlastni control na selectboxy kdyz to nette uz neumi, anebo se jedna o bug, ktery nekdo vbrzku opravi?

h4kuna
Backer | 740
+
0
-

Ahoj,
já jsem to vyzkoušel, viz můj kód:

$countries = [
    'Europe' => [
        'CZ' => 'Česká Republika',
        'SK' => 'Slovensko',
        'GB' => 'Velká Británie',
    ],
    'CA' => 'Kanada',
    'US' => 'USA',
    '?'  => 'jiná',
];

$form = new Nette\Forms\Form;
$form->addSelect('country', 'Země:', $countries)
    ->setPrompt('Zvolte zemi');

$form->addSubmit('send');

if($form->isSubmitted()) {
	dump($form->getValues());
	exit;
}

echo $form;

A výstup mám podle očekávání, testováno na nette/forms v2.4.4

Nette\Utils\ArrayHash #58f4
  country => NULL

Jaký objekt na formuláře používáš? Nemáš nějaké své rozšíření?

krucinal
Člen | 38
+
0
-

Chyba vylitne, kdyz se pokusim o setDefaults() na formu, nebo setDefaultValue() na select elementu.

h4kuna
Backer | 740
+
0
-

Zkusil jsem

$form->addSelect('country', 'Země:', $countries)
    ->setPrompt('Zvolte zemi')
	->setDefaultValue('SK');

a pak toto

$form->addSelect('country', 'Země:', $countries)
    ->setPrompt('Zvolte zemi');

$form->setDefaults(['country' => 'SK']);

Chování je správné. Žádná exception a buď dostanu vybranou hodnotu, defaultní nebo NULL když vyberu ‚Zvolte zemi‘.

Ukaž implementaci.

Editoval h4kuna (20. 4. 2017 8:38)

krucinal
Člen | 38
+
0
-

Ano, defaults na vybrane hodnote funguje. Chyba vznikne, kdyz se jako default pokusim nastavit hodnotu toho promptu.

Technicky – mam formular, get (nepodstatne) na vyhledavani. V mem pripade vyber mesta. Prompt obsahuje text ‚Vsechna mesta‘ a nette mu vygeneruje hodnotu ''.

Do formulare pres setDefaults() vkladam hodnoty dosle v url – muze byt i vychozi nastaveni vyhledavani pres url z nejakeho generovaneho odkazu, tedy nemusi se jednat o odeslany formular, ale vychozi hodnoty.

setDefaults() funguje se vsemi hodnotami selectu s vyjimkou toho promptu.

Kdyz zkusis setDefaultValue('') … to je hodnota toho promptu … vylitne ta vyjimka. To je ta vec, ktera driv fungovala a nyni konci s chybou.

Editoval krucinal (20. 4. 2017 8:39)

Jan Tvrdík
Nette guru | 2595
+
0
-

@krucinal Mělo by fungovat $form['countries']->checkAllowedValues = FALSE.

CZechBoY
Člen | 3608
+
+3
-

Od kdy se do prompt dava hodnota k vybrani? Prompt je hlaska, kterou reknes uzivatelovi at neco vybere.
Normalne dej vsechna mesta jako prvni polozku…

David Grudl
Nette Core | 8107
+
+2
-

setDefaultValue('') prostě nevolej, prompt je vybrany automaticky.

krucinal
Člen | 38
+
0
-

@DavidGrudl ok, potom ale nemuzu generalne vlozit cela data odeslana formularem do setDefaults() a naplnit tak formular, ale musim si nejdriv zkontrolovat, jestli nahodou nemaji data selectboxu hodnotu promptu (btw odeslanou formularem), coz je tak trosku jako drbat se levou rukou za pravym uchem. Vychazi mi z toho, ze nejlepsi bude tuhle featuru radsi nepouzivat a napsat si to po svem a predelat ted stovky formularu v projektu a misto promptu z nette si vsude rucne pridat tu prvni volbu do pole dat :-(

Kazdopadne diky vsem za tipy.

Jan Tvrdík
Nette guru | 2595
+
0
-

@krucinal Řešil jsem něco podobného (nastavuji do formuláře výchozí hodnotu z query parametrů, ale nedokáži snadno ověřit, že ta hodnota je dobře před nastavením) a nakonec jsem došel k následujícímu hacku (@DavidGrudl se teď možná trochu zděsí, co jsem vymyslel).

	private function trySetDefaultValue(BaseControl $control, $newValue): bool
	{
		assert(!$control->hasErrors(), 'errors may be lost by cleanErrors()');

		if ($newValue === NULL) {
			return FALSE;
		}

		$currentValue = $control->getValue();

		try {
			$control->setDefaultValue($newValue); // may throw
			$control->validate();

			if (!$control->hasErrors()) {
				return TRUE;
			}

		} catch (\Nette\InvalidArgumentException $e) {
			// suppress, probably not scalar or invalid value for select box
		}

		$control->cleanErrors();
		$control->setValue($currentValue);
		return FALSE;
	}
CZechBoY
Člen | 3608
+
0
-

Kdyz je ulozena hodnota null tak ji odeber z pole defaults, potom to nebude kricet.

David Grudl
Nette Core | 8107
+
0
-

krucinal napsal(a):

@DavidGrudl ok, potom ale nemuzu generalne vlozit cela data odeslana formularem do setDefaults() a naplnit tak formular

Tomu vůbec nerozumím. Do setDefaults() se dávají výchozí zobrazené hodnoty, než je formulář odeslán. Jakmile je odeslán, setDefaults se vůbec neřeší. Viz https://doc.nette.org/cs/forms

ale musim si nejdriv zkontrolovat, jestli nahodou nemaji data selectboxu hodnotu promptu

Co je to hodnota promptu? Prompt neber vůbec jako hodnotu.

Vychazi mi z toho, ze nejlepsi bude tuhle featuru radsi nepouzivat a napsat si to po svem a predelat ted stovky formularu v projektu a misto promptu z nette si vsude rucne pridat tu prvni volbu do pole dat :-(

To určitě není potřeba, jen myslím používáš formulář nějak jinak.

David Grudl
Nette Core | 8107
+
0
-

@JanTvrdík napsal(a):

@krucinal Řešil jsem něco podobného (nastavuji do formuláře výchozí hodnotu z query parametrů, ale nedokáži snadno ověřit, že ta hodnota je dobře před nastavením)

Mám pocit, že @krucinal spíš nějak nezvykle používá formulář, než že by řešil obecně problém neplatných parametrů.

a nakonec jsem došel k následujícímu hacku

Tak na to by určitě bylo lepší udělat nějakou BaseControl::isValueAllowed(), ne?

Jan Tvrdík
Nette guru | 2595
+
0
-

Tak na to by určitě bylo lepší udělat nějakou BaseControl::isValueAllowed(), ne?

Ano, to by bylo mnohem lepší.

David Grudl
Nette Core | 8107
+
0
-

Pošleš pr?

krucinal
Člen | 38
+
0
-

Ted uz jen ciste akademicky na vysvetleni, protoze jsem mozna ne uplne presne napsal o co mi slo.

Mozna pouzivam formular nezvykle, ale me se to jevi uplne normalni. Jen vyuzivam nette k vygenerovani/naplneni a zprocesovani formulare bez nejake vyssi logiky. Jedna se v tomto pripade o formular na vyhledavani (teoreticky se ale muze jednat o libovolny formular), ktery muze fungovat tak, ze sam neco odesle (pak setDefaults nejspis nepotrebuju), ale zaroven na stranku muze vest odkaz odjinud, kdy uz muzou existovat fragmenty dat v url (nebo jinem zdroji), potom je chci pouzit jako vychozi hodnoty.

Nejjednodussi co me napada je proste zavolat setDefaults generalne tam kde buduju formular a nemuset u toho neco hackovat/podminkovat.

Fakt, ze hodnota promptu nesmi byt odesilana je pro me nova informace, celou dobu jsem to bral jako nastroj pro prvni hodnotu selectboxu. Mam nekde nejake metody, ktere ziskavaji data pro selectbox, napr. z ciselniku. Ty ale vraci jen data, prvni polozka je veci formulare, jelikoz nekdy ji tam potrebuju, jindy ne, pokazde muze mit jiny text, specificky pro dany fomrular a pokazde muze mit jiny logicky vyznam.
Na tohle se mi prompt naramne hodil, nicene uz se nyni pouzit neda.

Vypnuti kontroly hodnot je sice reseni, ale nelibi se mi. Ta vestavena kontrola je vyborna vec.

Priklad:

protected function createComponentFilterForm()
    {
        ...
        ...
        $filterForm = new Form();

        $filterForm->setMethod(Form::GET);
        ....
        $filterForm->addSelect('city', 'Město', $tCities->getCityListPreferred())
            ->setPrompt('--- všechna města ---')
        ;
        ...
        ...
        $filterForm->addSubmit('submit', 'Odeslat');

        $filterForm->setDefaults($this->getParameters());

        $filterForm->onSuccess[] = [$this, 'filterFormSubmitted'];

        return $filterForm;
    }

    public function filterFormSubmitted() {
        ...
    }
F.Vesely
Člen | 368
+
0
-

Kdybys tam misto prazdneho stringu '' posilal NULL, tak ti to funguje dle ocekavani. Je to i logicke, pokud neni nic vybrane, tak SelectBox::getValue() vraci NULL, tudiz pokud nechces nic vybrat, tak mu posles NULL. Prompt je proste nic, zadna hodnota, nevybrano.

David Grudl
Nette Core | 8107
+
0
-

Už tomu rozumím.

Prompt v selectboxu není žádná hodnota, je to jen „popiska“. Pokud je prvek povinný, vybrat nejde, pokud je nepovinný, getValue() vrací NULL, jak píše @F.Vesely. Prázdný řetězec je nějaká interní nepodstatná záležitost, od které jsi odstíněný.

No tedy jen do doby, než sáhneš pod kapotu, do hrubých dat, což je právě $this->getParameters(). Ty je potřeba očistit, zvalidovat. Takže třeba '' nahradit za NULL, ale taktéž ověřit, že jsou stringy tam kde mají být stringy, pole kde mají být pole atd.

Fakt je, že tohle by mohl formulář umět, přijmout hrubá data, ale v tuto chvíli snadno neumí.