Profíci, jak tvoříte vlastní formulářové komponenty?

Upozornění: Tohle vlákno je hodně staré.

před 7 lety

David Grudl
Nette Core | 7148
+
0
-

Představte si, že chcete udělat formulářový prvek pro zadávání data, podobný jako používá Facebook, tedy s odděleným políčkem pro den, měsíc a rok (mohou to být selectboxy):

Api by mělo přijímat a vracet jako hodnotu objekt DateTime.

Jakým způsobem v současné verzi Nette podobný prvek navrhnete?

před 7 lety

Šaman
Člen | 2438
+
0
-

Vlastní kontejner, který obsahuje tři selektítka. Jen to API jsem zatím kontejneru dopsat nezkoušel.

před 7 lety

enumag
Člen | 2129
+
0
-

Jsem na tom stejně jako Šaman, už jsem nad tím přemýšlel dříve a také bych to řešil jako Container, ale nezkoušel jsem to v praxi.

level 2: Nechť se optiony v selectboxu „Den“ živě updatují dle vybraného měsíce (a v případě února i roku).

Editoval enumag (26. 6. 2013 19:09)

před 7 lety

Čelo
Člen | 42
+
0
-

Snažil bych se dostat k něčemu takovému:

<?php
$form->addDateSelect('geburtstag','Geburtstag');
?>

před 7 lety

Filip Procházka
Moderator | 4693
+
+1
-

Podědím BaseControl a napíšu metodu getControl tak, aby vytvořila 3 inputy (selecty). Potom setValue a getValue naučím pracovat s datetime.

Už tu třídu nepoužívám, ale pro představu cca takhle.

Editoval Filip Procházka (26. 6. 2013 23:19)

před 7 lety

Milo
Nette Core | 1153
+
0
-

Podobné inputy řeším cca takto. Jen dědím z LatteBaseControl, abych mohl getControl() provádět v Latte.

class DateInput extends Nette\Forms\Controls\BaseControl
{
	private $emptyValue = array('y' => '', 'm' => '', 'd' => '');



	public function getControl()
	{
		$value = $this->getHtmlValue();

		$d = Html::el('input')->name($this->name . '[d]')->value($value['d']);
		$m = Html::el('input')->name($this->name . '[m]')->value($value['m']);
		$y = Html::el('input')->name($this->name . '[y]')->value($value['y']);

		return Html::el()->add($d)->add($m)->add($y);
	}



	public function setValue($value)
	{
		if (is_array($value)) {
			$this->value = $value + $this->emptyValue;

		} elseif ($value instanceof \DateTime) {
			$this->value = array(
				'y' => $value->format('Y'),
				'm' => $value->format('m'),
				'd' => $value->format('d'),
			);

		} else {
			$this->value = NULL;
		}
		return $this;
	}



	protected function getHtmlValue()
	{
		if (is_array($this->value)) {
			return $this->value;
		}

		return $this->emptyValue;
	}



	public function getValue()
	{
		$value = $this->getHtmlValue();
		$value = \DateTime::createFromFormat('Y-m-d', "$value[y]-$value[m]-$value[d]");

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

		return $value;
	}

}

před 7 lety

Felix
Nette Core | 1034
+
0
-

@Milo: to mi prijde celkem pekne, ale dava to skrytou zavislost do $values, co udelat nejaky typehint do setValues()? Neco jako InputDatetimeFormat (vim, dlouhe, divne..)

před 7 lety

Milo
Nette Core | 1153
+
0
-

@Felix Skrytou závislostí myslíš ty indexy pole? To se používá pouze interně, když Nette nastavuje přijaté hodnoty. A typehint stejně nemůžeš, protože dědíš.

před 7 lety

hrach
Člen | 1818
+
0
-

Na signálech Jan Tvrdík před více jak 3 lety zavedl abstraktní formulářovou komponentu, ktera implementuje standardní komponentové rozhraní.

Jednotlivé formulářové komponenty pak mohou mít interně kolik chcou komponent.

V rámci nextras jsem před měsíci udělal WIP, ještě není dodělané, vypadá to takto:

date range picker .php
- date range picker .latte

Je to neuvěřitelně mocný nástroj, asi nejsložitější komponentu v tom máme takovou, který dokonce obsahuje popup. Komponenta saba o sobě umí nastavovat přístupová oprávnění pro skupiny, etc, ala Facebook: všechno žlutý je jedna formulářová komponenta $form->addAuthUtil(). Popup se zobrazí po vybrání položky upřesnit v hlavnim inputu…

Editoval hrach (27. 6. 2013 8:26)

před 7 lety

hrach
Člen | 1818
+
0
-

Jeste bych zapomnel: nejdulezitejsi featurou toho celeho je to, ze ta formularova komponenta, respektive jeji potomci muzou vytvaret odkazy na sebe a volat signaly, tzn. menit se ajaxem, etc… :)

před 7 lety

enumag
Člen | 2129
+
0
-

@hrach: Hezký, dělal jsem něco podobného když jsem upravoval TagsControl aby si suggest řídil ajaxem sám od sebe pomocí signálu. Tohle je ale propracovanější a vypadá to jako velmi dobrý základ pro budoucí formulářové elementy.

před 7 lety

Milo
Nette Core | 1153
+
0
-

Mě by akorát zajímalo, s čím vyrukuje David :-)

před 7 lety

enumag
Člen | 2129
+
0
-

@Milo: To já taky, ale nejvíc jsem zvědavej jestli opraví to co mi ve formulářích chybí:

  • praktická nemožnost přidávání vlastních validátorů včetně JS varianty
  • signál validate pro live validaci callback validátorů (např. kontrola zda uživatel již existuje)
  • možnost změny chybových zpráv automaticky přidaných pravidel (např. selectbox invalid value)

Editoval enumag (27. 6. 2013 13:14)

před 7 lety

Filip Procházka
Moderator | 4693
+
0
-

<ot>Největší bolest formulářů je v současnosti pevně svázaný validátor :)</ot>

Editoval Filip Procházka (27. 6. 2013 12:47)

před 7 lety

David Grudl
Nette Core | 7148
+
0
-

enumag: cože? Tohle je co? https://github.com/…alidator.php#L18. V té custom JS validaci si pak snadno můžeš třeba ověřit přes AJAX data.

Select invalidate message se změní v Nette\Forms\Rules::$defaultMessages. Navíc to není potřeba, ta zpráva se uživateli nezobrazí, jde o zabránění hackingu.

Filip: Tam přece žádný pevně svázaný validátor není. Všechny validace jsou ve třídě Validators. Ve formulářích je pak jen minimální obálka nad nimi. Lze to řešit líp?

před 7 lety

enumag
Člen | 2129
+
0
-

@David Grudl: Ok tak jsem to přehlédl. Když jsem to naposled zkoušel tak mi to nešlo, nevím už přesně proč.

před 7 lety

David Matějka
Moderator | 6202
+
0
-

@David Grudl: jednotlive validace oddelene jsou, ale snadno konfigurovatelny validator (ci seznam pravidel) je jen u formularu, se kterym je pevne svazany. nevim, jak to myslel Filip, ale mne by se libilo, aby validator sel napsat oddelene a byl by aplikovatelny nejen na form, ale treba jen na pole (napr. vstup z api), nedavno se to tu na foru i resilo, videl jsem i nejaky funkcni reseni, ale mohlo by to byt primo v nette :)
treba takto:

$validator = new Validator();
$validator->addText('foo')->addRule(Validator::FILLED)->addRule(Validator::MIN_LENGTH, 'Minimalni delka je %d znaku', 5);
$validator->addBoolean('bar');
....

$form = new Form();
$form->addText('foo', 'Foo');
$form->addCheckbox('bar', 'Bar');
$form->setValidator($validator);

//nebo treba z API ziskam nejaka data, tak
$result = $validator->validate($data);
//a v result bude stav a pripadne seznam chyb

před 7 lety

Filip Procházka
Moderator | 4693
+
0
-

@David Grudl: Formuláře je můžou využívat, ale měly by jít použít samostatně. Což teď jdou velice težko.

V ideálním případě bych měl nějak nadefinované validace (fluentem nad nějakým rules objektem, annotacemi, …), přes validátor který tyto definice bude mít načtené bych prohnal strukturu (formulář, entitu, pole, …) a vypadl by mi výsledek (violations) který by obsahoval chybové zprávy a ke kterému prvku patří ($violations[email] = "neobsahuje @") a tyto pravidla by se aplikovaly na formulář (protože vím jméno prvku, můžu chybu přiřadit ke správnému políčku a správně vykreslit).

// pseudokód
$form->onValidate[] = function ($form) use ($entity) {
	$entity->setValues($form->values);
	$validator = new Validator(new MetadataReader($entity));
	$result = $validator->validate($entity);
	$form->addErrors($result);
};

Inspirace

Co tím získám? Zcela jasné oddělení validátoru, možnost plně jej využít i v modelových třídách nad entitami, validovat json strukturu co přijde v API, …

Editoval Filip Procházka (28. 6. 2013 2:40)

před 7 lety

llook
Člen | 411
+
0
-

Doteď jsem tohle dělal zhruba tak, že jsem si těch několik HTML inputů definoval v getControl() a jako name jsem jim dal $this->htmlName . '["year"]' apod. A potom překlad z array na cílový objekt a zpět v getValue()/setValue().

Ale to řešení s controlem implementujícím IContainer se mi líbí víc.

před 7 lety

Šaman
Člen | 2438
+
0
-

Nějak to tu utichlo, neznamená to doufám, že se David skutečně ptal jak na to? :)
Plánuje do 2.1 nějaké kchůl řešení, nebo dokonce poradit best practise na již existující verzi?

před 7 lety

David Matějka
Moderator | 6202
+
0
-

David neco planuje s formularema, rikal to na posobote, nechtel ale prozradit co :)

před 7 lety

enumag
Člen | 2129
+
0
-

@David Grudl: Tak už jsem si vzpomněl co mi u vlastních validátorů tak zásadně chybí. Není možné přes config nastavit default message. Nebo aspoň ne pomocí NetteExtension. Když nad tím tak přemýšlím tak mne nenapadá jak to kamkoli do configu napsat (bez použití vlastní extension).

Editoval enumag (25. 7. 2013 19:51)

před 7 lety

Filip Procházka
Moderator | 4693
+
0
-

@enumag: to není pravda

před 7 lety

enumag
Člen | 2129
+
0
-

@Filip Procházka: Přečti si znovu můj příspěvek a ten kód. ;-)

před 7 lety

Filip Procházka
Moderator | 4693
+
0
-

@enumag: nechápu, pletu se v nečem? :)

před 7 lety

enumag
Člen | 2129
+
0
-

@Filip Procházka: Všimni si tohoto řádku z NetteExtension:

$initialize->addBody('Nette\Forms\Rules::$defaultMessages[Nette\Forms\Form::?] = ?;', array($name, $text));

Kvůli tomu Nette\Forms\Form::? tohle nelze použít na žádný validátor který není jako konstanta přímo v Nette\Forms\Form. Když si napíšu vlastní validátor na dejme tomu telefonní číslo tak ten tam tu konstantu nemá a default msg přes config nenastavím aniž bych psal své Extension.

před 7 lety

Filip Procházka
Moderator | 4693
+
0
-

@enumag: good point, máš pravdu :)

před 7 lety

Milo
Nette Core | 1153
+
0
-

@David Grudl Právě jsem shlédl video z 56. Poslední soboty kde jsi nechal nakouknout do formulářů ve 2.1 a je to super. U jedné aplikace potřebuji přepsat systém přidělování oprávnění a tam se to hodí perfektně.

před 7 lety

ViPEr*CZ*
Člen | 798
+
0
-

Milo napsal(a):

@David Grudl Právě jsem shlédl video z 56. Poslední soboty kde jsi nechal nakouknout do formulářů ve 2.1 a je to super. U jedné aplikace potřebuji přepsat systém přidělování oprávnění a tam se to hodí perfektně.

+1