Změna renderování checkboxu uvnitř Label

ajda2
Člen | 66
+
+1
-

Zdravím Vás,
narazil jsem na menší problém při tvorbě „vlastního“ rendereru pro formuláře.
Mým cílem je změnit výchozí vykreslování checkboxu uvnitř labelu na tento tvar:

<label for="checkbox-id">
	<input type="checkbox" name="active" id="checkbox-id">
	<span class="nejaka-trida">TENTO SPAN CHCI VLOŽIT VČETNĚ TŘÍDY</span>
	Label checkboxu
</label>

Za tímto účelem jsem si vytvořil vlastní renderer, který dědí od https://api.nette.org/…enderer.html abych nemusel začínat na zelené louce.
Přetížil jsem metodu renderControl, abych v ní vložil do labelu svůj kýžený span.
Zde jsem právě narazil na problém.

<?php
$el = $control->getControl();
if ($el instanceof Html && $el->getName() === 'input') {
	$el->class($this->getValue("control .$el->type"), TRUE);
}

return $body->setHtml($el . $description . $this->renderErrors($control));
?>

Konkrétně volání metody getControl() způsobí, že veškeré mnou vložené HTML elementy do Labelu jsou nenávratně ztraceny, protože se nastaví HTML obsah docela brutálním způsobem napřímo pomocí metody setHtml jako řetězec a není jej možné poté snadno editovat, protože veškerý obsah je řetězec.
Viz https://api.nette.org/…box.php.html#…

Nenapadá někoho prosím nějaké jednoduché řešení, jak vložit nějaký HTML obsah dovnitř Label za samotný checkbox?
Řešením by nejspíš bylo změnit drastické vkládání HTML obsahu skrze metodu setHtml a nahradit ji insert, nebo add, ale vytvářet vlastní checkBox třídu a vlastní Form, který jej bude používat mi přijde zbytečně pracné.

Předem děkuji za pomoc a rady.

Editoval ajda2 (30. 6. 2016 8:36)

Oli
Člen | 1215
+
+3
-

Jde změnit nějak líp renderování checkboxu (selectu, radio listu)?

Potřeboval bych kvůli material designu například aby v checkboxu input nebyl obalenej labelem. Renderer jsem si napsal, ale do něj jde z Control\Checkbox už hotovej řetězec <label><input/></label>.

To by znamenalo napsat vlastní implementaci Control\Checkboxu a UI\Form. Pokud bych chtěl ale použít jinej form, musel bych to definovat znovu, takže vlastně bych měl udělat i vlastní Form\Form a Forms\Container.

Není to trochu zbytečné? Neexistuje nějaký parametr, něco, který by zajistil, že se input nebude obalovat do label?

EDIT:

Jak to tak bývá, sotva se zeptám, najdu řešení. Checkbox i radio list mají metodu getControlPart a getLabelPart. Takže pak stačí v renderControl něco takového:

if ($control instanceof Nette\Forms\Controls\Checkbox)
{
    $el = $control->getControlPart();
} else {
    $el = $control->getControl();
}

Editoval Oli (15. 11. 2016 8:29)

steelbull
Člen | 241
+
0
-

@Oli Presne to isté by som potreboval aj ja, vyriešil si to nejako?

Oli napsal(a):

Jde změnit nějak líp renderování checkboxu (selectu, radio listu)?

Potřeboval bych kvůli material designu například aby v checkboxu input nebyl obalenej labelem. Renderer jsem si napsal, ale do něj jde z Control\Checkbox už hotovej řetězec <label><input/></label>.

To by znamenalo napsat vlastní implementaci Control\Checkboxu a UI\Form. Pokud bych chtěl ale použít jinej form, musel bych to definovat znovu, takže vlastně bych měl udělat i vlastní Form\Form a Forms\Container.

Není to trochu zbytečné? Neexistuje nějaký parametr, něco, který by zajistil, že se input nebude obalovat do label?

EDIT:

Jak to tak bývá, sotva se zeptám, najdu řešení. Checkbox i radio list mají metodu getControlPart a getLabelPart. Takže pak stačí v renderControl něco takového:

if ($control instanceof Nette\Forms\Controls\Checkbox)
{
    $el = $control->getControlPart();
} else {
    $el = $control->getControl();
}

Editoval steelbull (25. 2. 2018 10:20)

steelbull
Člen | 241
+
0
-

@Oli Ja potrebujem vyrenderovať toto:

<input type="checkbox" id="1" class="k-checkbox" checked="checked">
<label class="k-checkbox-label" for="1">Rear side airbags</label>

Oli napsal(a):

Jde změnit nějak líp renderování checkboxu (selectu, radio listu)?

Potřeboval bych kvůli material designu například aby v checkboxu input nebyl obalenej labelem. Renderer jsem si napsal, ale do něj jde z Control\Checkbox už hotovej řetězec <label><input/></label>.

To by znamenalo napsat vlastní implementaci Control\Checkboxu a UI\Form. Pokud bych chtěl ale použít jinej form, musel bych to definovat znovu, takže vlastně bych měl udělat i vlastní Form\Form a Forms\Container.

Není to trochu zbytečné? Neexistuje nějaký parametr, něco, který by zajistil, že se input nebude obalovat do label?

EDIT:

Jak to tak bývá, sotva se zeptám, najdu řešení. Checkbox i radio list mají metodu getControlPart a getLabelPart. Takže pak stačí v renderControl něco takového:

if ($control instanceof Nette\Forms\Controls\Checkbox)
{
    $el = $control->getControlPart();
} else {
    $el = $control->getControl();
}
jurajkovac
Člen | 2
+
+1
-

Tu je riešenie, ak sa nájde niekto ďalší, kto potrebuje vykresľovať checkbox/radio button a label za sebou.

1. Jednoduchá, ale pracnejšia cesta: low-level rendering
Namiesto prostého {control myForm} si všetko vymenujem ručne:

{form myForm}
	{input choices:itemA}{label choices:itemA /}
	{input choices:itemB}{label choices:itemB /}
	...
{/form}

Samozrejme je možné to zabaliť do cyklu a vymenovať automaticky:

{form myForm}
	{foreach $form['choices']->items as $key => $label}
		{input choices:$key}{label choices:$key /}
	{/foreach}
{/form}

2. Náročnejšia, ale udržateľnejšia cesta: custom renderer
Vytvorím si vlastný form renderer, ktorý zdedí všetky schopnosti od DefaultFormRenderer, avšak pre radio buttons a checkboxy bude uplatňovať vlastnú logiku vykresľovania.

Vytvorím si teda novú triedu ako extension Nette\Forms\Rendering\DefaultFormRenderer a úplne stačí, aby obsahovala jedinú metódu: renderControl()
Ideálne skopírovať ju kompletne z DefaultFormRendereru a len v nej ošetríme konkrétne prípady pre RadioList a Checkbox:

class myFormRenderer extends Nette\Forms\Rendering\DefaultFormRenderer
	public function renderControl(Nette\Forms\IControl $control) {
		...
		$control->setOption('rendered', true);

		// Is this an instance of a RadioList or Checkbox?
		if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\RadioList) {
			// Create an empty Html container object
			$el = Html::el();
			// Get all the child items
			$items = $control->getItems();
			// For each child item, add the appropriate control part and label part after one another
			foreach($items as $key => $item) {
				$el->addHtml($control->getControlPart($key));
				$el->addHtml($control->getLabelPart($key));
			}
		// For all other control types, revert to default functionality
		} else {
			$el = $control->getControl();
		}
		...
	}

Snáď to pomôže každému, kto potrebuje použiteľný input[type=radio]+label CSS selektor.
Poďakovanie patrí @Oli, ktorý ma priviedol na správnu stopu.

steelbull
Člen | 241
+
0
-

jurajkovac napsal(a):

Tu je riešenie, ak sa nájde niekto ďalší, kto potrebuje vykresľovať checkbox/radio button a label za sebou.

1. Jednoduchá, ale pracnejšia cesta: low-level rendering
Namiesto prostého {control myForm} si všetko vymenujem ručne:

{form myForm}
	{input choices:itemA}{label choices:itemA /}
	{input choices:itemB}{label choices:itemB /}
	...
{/form}

Samozrejme je možné to zabaliť do cyklu a vymenovať automaticky:

{form myForm}
	{foreach $form['choices']->items as $key => $label}
		{input choices:$key}{label choices:$key /}
	{/foreach}
{/form}

2. Náročnejšia, ale udržateľnejšia cesta: custom renderer
Vytvorím si vlastný form renderer, ktorý zdedí všetky schopnosti od DefaultFormRenderer, avšak pre radio buttons a checkboxy bude uplatňovať vlastnú logiku vykresľovania.

Vytvorím si teda novú triedu ako extension Nette\Forms\Rendering\DefaultFormRenderer a úplne stačí, aby obsahovala jedinú metódu: renderControl()
Ideálne skopírovať ju kompletne z DefaultFormRendereru a len v nej ošetríme konkrétne prípady pre RadioList a Checkbox:

class myFormRenderer extends Nette\Forms\Rendering\DefaultFormRenderer
	public function renderControl(Nette\Forms\IControl $control) {
		...
		$control->setOption('rendered', true);

		// Is this an instance of a RadioList or Checkbox?
		if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\RadioList) {
			// Create an empty Html container object
			$el = Html::el();
			// Get all the child items
			$items = $control->getItems();
			// For each child item, add the appropriate control part and label part after one another
			foreach($items as $key => $item) {
				$el->addHtml($control->getControlPart($key));
				$el->addHtml($control->getLabelPart($key));
			}
		// For all other control types, revert to default functionality
		} else {
			$el = $control->getControl();
		}
		...
	}

Snáď to pomôže každému, kto potrebuje použiteľný input[type=radio]+label CSS selektor.
Poďakovanie patrí @Oli, ktorý ma priviedol na správnu stopu.

  1. ten prvý prípad je pre mňa príliš zložitý, pretože tých checkboxov mám v množstve formulárov veľa. A potrebujem unifikované riešenie, ktoré potrebujem implementovať do viacerých projektov.
  2. ten druhý spôsob je pre mňa nepoužiteľný, pretože formulár renderujem nasledovne a pri manuálnom renderovaní sa FormRenderer neuplatňuje:

FormControl:

$form->addCheckbox('fin_soft_saving', $t->trans('project.form.fin_soft_saving'));

$form->addCheckbox('fin_ematrix', $t->trans('project.form.fin_ematrix'))
    ->setRequired(false);

Výsledok:

<div class="row">
                    <div class="col-md-3">

                        <label for="frm-form-form-fin_soft_saving"><input name="fin_soft_saving" id="frm-form-form-fin_soft_saving" type="checkbox">Soft saving?</label>
                        <span class="form-error-message"></span>
                    </div>
                    <div class="col-md-3">

                        <label for="frm-form-form-is_checked_by_CD"><input name="is_checked_by_CD" id="frm-form-form-is_checked_by_CD" type="checkbox">Overené CD?</label>
                        <span class="form-error-message"></span>
                    </div>
                    <div class="col-md-3">

                        <label for="frm-form-form-fin_ematrix"><input name="fin_ematrix" id="frm-form-form-fin_ematrix" type="checkbox">E-matica?</label>
                        <span class="form-error-message"></span>
                    </div>
                </div>
jurajkovac
Člen | 2
+
0
-

steelbull napsal(a):

2. ten druhý spôsob je pre mňa nepoužiteľný, pretože formulár renderujem nasledovne a pri manuálnom renderovaní sa FormRenderer neuplatňuje:

A ako vyzerá kód v Latte?