Rozšíření formuláře o netriviální BaseControl

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

Ahoj,

chtěl jsem rozšířit formuláře o vylepšenou verzi MultiSelectBoxu. Zaregistrování rozšíření bylo snadné, dělám to takto:

<?php
	public static function register() {
		$class = __CLASS__;
		\Nette\Forms\Container::extensionMethod('addCleverMultiSelectBox', function (\Nette\Forms\Container $form, $name, $label = null, $emptyLabel = null, array $items = null, array $selectedItems = null, $messageAlreadyContained = null, $messageNotInList = null, $additionHandler = null) use ($class) {
			$component = new $class($label, $emptyLabel, $items, $selectedItems, $messageAlreadyContained, $messageNotInList, $additionHandler);
			$form->addComponent($component, $name);
			return $component;
		});
	}
?>

Kde mi ale Nette klade odpor je v tom, že tenhle vylepšený MultiSelectBox obsahuje docela dost HTML elementů. To, že tu komponentu vkládám do formuláře mě nutí používat HTML elementy v metodě getControl. To mi ale přijde jako prasárna (ten kód s takto budvaným HTML je ve skutečnosti 10× delší a stále roste):

<?php
            $cleverMultiSelectBox = Html::el('div');
            $typeAhead = Html::el('input');
            $typeAhead->attrs['type'] = 'text';
            ...
            $cleverMultiSelectBox->add($typeAhead);
            $cleverMultiSelectBox->class = 'cleverMultiSelectBox';
            return $cleverMultiSelectBox;
?>

Ideální by bylo, kdybych uměl nějak nainlinovat normální komponentu do formulářové komponenty. Takto mi nezbývá než všechno vkládání HTML buď dělat tím šíleným způsobem v getControl() nebo to nabastlit do JavaScriptu. Ani jedno se mi nelíbí, když používám Nette, chci to přirozeně psát v Latte.

Tedy otázka zní: Lze nějak takový vylepšený MultiSelectBox napsat jako plnohodnotnou formulářovou komponentu a zároveň veškerý HTML kontext komponenty napsat v Latte?

Pavel

Felix
Nette Core | 1245
+
0
-

Pisu z hlavy, ale co takhle.

// Zbavit se tolika parametru do jednoho Configuration..

public static function register() {
    $class = __CLASS__;
    \Nette\Forms\Container::extensionMethod('addCleverMultiSelectBox', function (\Nette\Forms\Container $form, $name, ComponentConfiguration $configuration) use ($class) {
        $component = new $class($configuration);
        $form->addComponent($component, $name);
        return $component;
    });
}

protected function createComponentForm() {
	   $form = new Form();
	   $form->addCleverMultiSelectBox('myName', $settings);
	   return $form;
}

// V getControl() muzes prece nacist sablonu normalne

public function getControl() {
	$template = $this->createTemplate(); // asi budes muset zaridit nejak sam, nebo predat v configuration
    $template->setFile(..);
    return $template->__toString();
}

Editoval Felix (17. 8. 2013 16:03)

Filip Procházka
Moderator | 4668
+
0
-

pavel.simecek napsal(a):

Kde mi ale Nette klade odpor je v tom, že tenhle vylepšený MultiSelectBox obsahuje docela dost HTML elementů. To, že tu komponentu vkládám do formuláře mě nutí používat HTML elementy v metodě getControl. To mi ale přijde jako prasárna (ten kód s takto budvaným HTML je ve skutečnosti 10× delší a stále roste):

Na tom není vůbec nic prasáckého :)

pavel.simecek
Člen | 11
+
0
-

// V getControl() muzes prece nacist sablonu normalne

public function getControl() {
$template = $this->createTemplate(); // asi budes muset zaridit nejak sam, nebo predat v configuration
$template->setFile(..);
return $template->__toString();
}

Jenže getControl nevrací String nýbrž objekt typu HTML. Takže takto by to podle mě nešlo.

pavel.simecek
Člen | 11
+
0
-

Filip Procházka napsal(a):

pavel.simecek napsal(a):

Kde mi ale Nette klade odpor je v tom, že tenhle vylepšený MultiSelectBox obsahuje docela dost HTML elementů. To, že tu komponentu vkládám do formuláře mě nutí používat HTML elementy v metodě getControl. To mi ale přijde jako prasárna (ten kód s takto budvaným HTML je ve skutečnosti 10× delší a stále roste):

Na tom není vůbec nic prasáckého :)

Pokud v tom nebyla ironie, tak musím říct, že si myslím, že to je prasárna. Představ si tenhle snippet přepsaný do PHP příkazů vytvářejících HTML strom:

<?php
  <div id="myModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
     <div class="modal-header">
       <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
       <h3 id="myModalLabel">Modal header</h3>
     </div>
     <div class="modal-body">
        <p>One fine body…</p>
     </div>
     <div class="modal-footer">
       <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
       <button class="btn btn-primary">Save changes</button>
     </div>
   </div>
?>

Samozřejmě pro stávající malé formulářové komponenty vytváření HTML stromu v PHP stačí. Prasácké je to jen v případě větších komponent se spoustou dodatečných HTML elementů.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Je to otázkou, zdali tohle má být zrovna formulářová komponenta (tedy součást formuláře). Není to spíš celý formulář?

pavel.simecek
Člen | 11
+
0
-

Ahoj Vojto,

Ne. Jen potřebuju nahradit běžný MultiSelectBox věcí, která vypadá podobně jako widget na výběr skills v LinkedIn. Běžný MultiSelectBox je pro uživatele dost těžko ovladatelná komponenta. Už mi to funguje a až to učešu do publikovatelné podoby, rád to zvěřejním včetně všech zdrojáků pro obecné použití.

Výše popsaný problém jsem vyřešil tím, že HTML jsem hodil jako řetězec do JavaScriptové části té komponenty a appenduju to na vhodné místo přes jQuery. Ale moc hrdej na takový řešení pochopitelně nejsem. Nejlepší, co mě napadá, je poslat tomu JavaScriptu na klientovi řetězec Latte šablony přes Ajax, ale to je taky dost děsivý řešení :-)

Pavel.

vojtech.dobes napsal(a):

Je to otázkou, zdali tohle má být zrovna formulářová komponenta (tedy součást formuláře). Není to spíš celý formulář?

dady
Člen | 12
+
0
-

Ahoj,

narazil jsem na podobný problém.
Snažím se vytvořit vylepšený uploader určený k vkládání obrázků umožňující zobrazení náhledu apod.
Chtěl jsem použít Jasny Bootstrap File Upload, takže bych potřeboval, aby byl klasický input[type=file] obklopen příslušným HTML kódem.

Jak tedy vnutit vlastní šablonu určitému formulářovému prvku (v mém případě Nette\Forms\Controls\UploadControl)?
Rád bych se pokud možno vyhnul poslednímu Pavlovu řešení s JavaScriptem.

Abych naznačil, kde se chci ubírat a jaké jsou mé pokusy, přikládám kód:

class ImageUploadControl extends UploadControl
{
    public static function register($method = 'addImageUpload')
    {
        Container::extensionMethod($method, function(Container $container, $name, $label)
        {
            return $container[$name] = new ImageUploadControl($label);
        });
    }

    public function getControl()
    {
        $control = parent::getControl();

        $jasnyBootstrapFileUploadTemplate = '
            <div class="fileupload fileupload-new" style="text-align: left;" data-provides="fileupload">
                <div class="fileupload-new thumbnail" style="max-width: 73px; max-height: 85px;">
                    <img class="participant-image" src="http://www.placehold.it/73x85/FFFFFF/AAAAAA">
                </div>
                <div class="fileupload-preview fileupload-exists thumbnail" style="max-width: 73px; max-height: 85px;"></div>
                <span class="btn btn-file">
                    <span class="fileupload-new">Vyber obrázek</span>
                    <span class="fileupload-exists">Změň</span>
                    '.$control.'
                </span>
                <a class="btn fileupload-exists" data-dismiss="fileupload" href="#">Odstraň</a>
            </div>
        ';
        $control->setHtml($jasnyBootstrapFileUploadTemplate);

        return $control;
    }
}

Jak je zřejmé – do šablony se pokouším „vecpat“ parent::getControl().
To je jistě nesmysl, ale pro ilustraci to asi stačí :o)

Jelikož jsem začátečník, uvítám jakékoliv bližší nasměrování či radu.
Děkuji!

Dady

dady
Člen | 12
+
0
-

Tak už jsem na to přišel :o)
Třeba se bude hodit někomu dalšímu…

public function getControl()
{
    $parentControl = parent::getControl();

    $jasnyBootstrapFileUploadTemplate = '
        <div class="fileupload fileupload-new" style="text-align: left;" data-provides="fileupload">
            <div class="fileupload-new thumbnail" style="max-width: 73px; max-height: 85px;">
                <img class="participant-image" src="http://www.placehold.it/73x85/FFFFFF/AAAAAA">
            </div>
            <div class="fileupload-preview fileupload-exists thumbnail" style="max-width: 73px; max-height: 85px;"></div>
            <span class="btn btn-file">
                <span class="fileupload-new">Vyber obrázek</span>
                <span class="fileupload-exists">Změň</span>
                '.$parentControl->render().'
            </span>
            <a class="btn fileupload-exists" data-dismiss="fileupload" href="#">Odstraň</a>
        </div>
    ';
    $control = Html::el($this->wrapper)->class('imageUploadControl');
    $control->setHtml($jasnyBootstrapFileUploadTemplate);

    return $control;
}