contributte / forms-multiplier

:repeat: Form multiplier & replicator for Nette Framework
https://contributte.org/packages/contributte/forms-multiplier.html
MIT License
26 stars 21 forks source link

Component 'dependent_select' is not attached to 'Nette\Forms\Form' #12

Closed BigOHenry closed 6 years ago

BigOHenry commented 7 years ago

Hello, i have problem with using multiplier and Nasext/DependentSelectBox. When i want to add new set of attributes with add button, i got this error: Component 'dependent_select' is not attached to 'Nette\Forms\Form'

Here is my testing component:

<?php

namespace App\Forms;

use NasExt\Forms\DependentData;
use Nette;
use Nette\Application\UI\Control;
use Nette\Application\UI\Form;
use WebChemistry\Forms\Controls\Submitter;

/**
 * Class MultiplierDependentFormControl
 * @package App\Forms
 */
class MultiplierDependentFormControl extends Control {

    public $onSave = array();

    /**
     * MultiplierDependentFormControl constructor.
     */
    public function __construct() {
        parent::__construct();
    }

    /**
     *
     */
    protected function beforeRender() {
        parent::beforeRender();
        if($this->isAjax()) $this->template->getLatte()->addProvider('formsStack', [$this['multiplierDependentForm']]);
    }

    /**
     *
     */
    public function render() {
        $this['multiplierDependentForm']->render();
        //$this->template->setFile(__DIR__ . '/multiplierDependentForm.latte');
        //$this->template->render();
    }

    /**
     * @return Form
     */
    public function createComponentMultiplierDependentForm() {

        $form = new Form;
        $copies = 1;
        $maxCopies = 10;

        $array = array(
            1 => 'jedna',
            2 => 'dva',
            3 => 'tři',
        );

        $lookup_data = array(
            1 => array (
                1 => '1/1',
                2 => '1/2',
                3 => '1/3',

            ),
            2 => array (
                1 => '2/1',
                2 => '2/2',
                3 => '2/3',
            ),
            3 => array (
                1 => '3/1',
                2 => '3/2',
                3 => '3/3',
            ),
        );

        $multiplier = $form->addMultiplier('multiplier', function (Nette\Forms\Container $container, Nette\Forms\Form $form) use ($array, $lookup_data) {
            $container->addSelect('array', 'Array', $array)
                ->setPrompt('--- Select ---');

            $container->addDependentSelectBox('dependent_select', 'Dependent select:', $container['array'])
                ->setDependentCallback(function ($values) use ($array, $lookup_data) {
                    $data = new DependentData;

                    if ($values['array']) {
                        $data->setItems($lookup_data[$values['array']])->setPrompt('---');
                    } else {
                        $data->setItems(array())->setPrompt('---');
                    }

                    return $data;
                })
                ->setPrompt('--- Select array first ---');

        }, $copies, $maxCopies);

        $presenter = $this;

        $multiplier->addCreateButton('+', 1, function (Submitter $submitter) use ($presenter) {
            //$submitter->setHtmlAttribute('class', 'ajax');
            $submitter->onClick[] = function () use ($presenter) {
                /** @var \Nette\Application\UI\Presenter $presenter */
                $presenter->redrawControl('multiplierDependentForm');
            };
        });

        $multiplier->addRemoveButton('-', function (Nette\Forms\Controls\SubmitButton $submitter) use ($presenter) {
            //$submitter->setHtmlAttribute('class', 'ajax');
            $submitter->onClick[] = function () use ($presenter) {
                /** @var \Nette\Application\UI\Presenter $presenter */
                $presenter->redrawControl('multiplierDependentForm');
            };
        });

        $form->addProtection('Error, try again!');

        $form->addSubmit('save', 'Uložit')
            ->setAttribute('class', 'btn-primary')
            ->setAttribute('id','submit')
            ->onClick[] = array($this, 'formSucceeded');

        return $form;
    }

    /**
     * @param $form
     * @param $values
     */
    public function formSucceeded($form, $values) {

        $this->onSave();
    }

}//class

/**
 * Interface IMultiplierDependentFormControl
 * @package App\Forms
 */
interface IMultiplierDependentFormControl {

    /**
     * @return MultiplierDependentFormControl
     */
    public function create();
}
MartkCz commented 7 years ago

If you want use this extension with dependent select from nasext, you must use the hack:

public function createComponentMultiplierDependentForm($name) {
        ....

        $multiplier->addRemoveButton('-', function (Nette\Forms\Controls\SubmitButton $submitter) use ($presenter) {
            //$submitter->setHtmlAttribute('class', 'ajax');
            $submitter->onClick[] = function () use ($presenter) {
                /** @var \Nette\Application\UI\Presenter $presenter */
                $presenter->redrawControl('multiplierDependentForm');
            };
        });

        $signalName = $name . '-' . $multiplier->getName();
        if (is_array($this->getSignal()) && $this->getSignal()[1] === 'load' && preg_match('#' . $signalName . '-([0-9]+)#', $this->getSignal()[0], $matches)) {
            $multiplier->addCopy($matches[1]);
        }
BigOHenry commented 7 years ago

Thanks you. I tried but i am getting this error now: Call to undefined method App\Forms\MultiplierDependentFormControl::getSignal().

MartkCz commented 7 years ago

So you must rewrite to:

$signalName = $this->getUniqueId() . '-' . $name . '-' . $multiplier->getName();
        if (is_array($this->getPresenter()->getSignal()) && $this->getPresenter()->getSignal()[1] === 'load' && preg_match('#' . $signalName . '-([0-9]+)#', $this->getPresenter()->getSignal()[0], $matches)) {
            $multiplier->addCopy($matches[1]);
        }

I don't try this code and you must update to newer version

BigOHenry commented 7 years ago

Thank you, but another error now (when i click on add button): Call to undefined method parent::addCopy().

MartkCz commented 7 years ago

Have you the newest dev-version? In old version addCopy is protected

BigOHenry commented 7 years ago

Yeah, its much better now :) Without ajax is working very well. But when i tried it with ajax, its not perfect yet:

Component:

<?php

namespace App\Forms;

use NasExt\Forms\DependentData;
use Nette;
use Nette\Application\UI\Control;
use Nette\Application\UI\Form;
use WebChemistry\Forms\Controls\Submitter;

/**
 * Class MultiplierDependentFormControl
 * @package App\Forms
 */
class MultiplierDependentFormControl extends Control {

    public $onSave = array();

    /**
     * MultiplierDependentFormControl constructor.
     */
    public function __construct() {
        parent::__construct();
    }

    /**
     *
     */
    protected function beforeRender() {
        parent::beforeRender();
        if($this->isAjax()) $this->template->getLatte()->addProvider('formsStack', [$this['multiplierDependentForm']]);
    }

    /**
     *
     */
    public function render() {
        //$this['multiplierDependentForm']->render();
        $this->template->setFile(__DIR__ . '/multiplierDependentForm.latte');
        $this->template->render();
    }

    /**
     * @param $name
     * @return Form
     */
    public function createComponentMultiplierDependentForm($name) {

        $form = new Form;
        $copies = 1;
        $maxCopies = 10;

        $array = array(
            1 => 'jedna',
            2 => 'dva',
            3 => 'tři',
        );

        $lookup_data = array(
            1 => array (
                1 => '1/1',
                2 => '1/2',
                3 => '1/3',

            ),
            2 => array (
                1 => '2/1',
                2 => '2/2',
                3 => '2/3',
            ),
            3 => array (
                1 => '3/1',
                2 => '3/2',
                3 => '3/3',
            ),
        );

        $multiplier = $form->addMultiplier('multiplier', function (Nette\Forms\Container $container, Nette\Forms\Form $form) use ($array, $lookup_data) {
            $container->addSelect('array', 'Array', $array)
                ->setPrompt('--- Select ---');

            $container->addDependentSelectBox('dependent_select', 'Dependent select:', $container['array'])
                ->setDependentCallback(function ($values) use ($array, $lookup_data) {
                    $data = new DependentData;

                    if ($values['array']) {
                        $data->setItems($lookup_data[$values['array']])->setPrompt('---');
                    } else {
                        $data->setItems(array())->setPrompt('---');
                    }

                    return $data;
                })
                ->setPrompt('--- Select array first ---');

        }, $copies, $maxCopies);

        $presenter = $this;

        $multiplier->addCreateButton('+', 1, function (Submitter $submitter) use ($presenter) {
            $submitter->setHtmlAttribute('class', 'ajax');
            $submitter->onClick[] = function () use ($presenter) {
                /** @var \Nette\Application\UI\Presenter $presenter */
                $presenter->redrawControl('multiplierDependentForm');
            };
        });

        $multiplier->addRemoveButton('-', function (Nette\Forms\Controls\SubmitButton $submitter) use ($presenter) {
            $submitter->setHtmlAttribute('class', 'ajax');
            $submitter->onClick[] = function () use ($presenter) {
                /** @var \Nette\Application\UI\Presenter $presenter */
                $presenter->redrawControl('multiplierDependentForm');
            };
        });

        $signalName = $this->getUniqueId() . '-' . $name . '-' . $multiplier->getName();
        if (is_array($this->getPresenter()->getSignal()) && $this->getPresenter()->getSignal()[1] === 'load' && preg_match('#' . $signalName . '-([0-9]+)#', $this->getPresenter()->getSignal()[0], $matches)) {
            $multiplier->addCopy($matches[1]);
            //$presenter->redrawControl('multiplierDependentForm');
        }

        $form->addProtection('Error, try again!');

        $form->addSubmit('save', 'Uložit')
            ->setAttribute('class', 'btn-primary')
            ->setAttribute('id','submit')
            ->onClick[] = array($this, 'formSucceeded');

        return $form;
    }

    /**
     * @param $form
     * @param $values
     */
    public function formSucceeded($form, $values) {

        $this->onSave();
    }

}//class

/**
 * Interface IMultiplierDependentFormControl
 * @package App\Forms
 */
interface IMultiplierDependentFormControl {

    /**
     * @return MultiplierDependentFormControl
     */
    public function create();
}

latte for the component:

{form multiplierDependentForm}
    <br> <br> <br> <br> <br> <br> <br> <br>
    <div class="col-md-12">
    {snippet multiplierDependentForm}
        {var $form = $control['multiplierDependentForm']}
        {var $oldGroup = ''}

        {foreach $form->getComponents() as $sid => $cntrl}

            {if $cntrl instanceof WebChemistry\Forms\Controls\Multiplier}

                <div class="col-md-12">
                    <br>
                    <h3>-----</h3>
                    <hr>
                </div>
                {foreach $cntrl->getControls() as $key => $c}
                    {if $c instanceof Nette\Forms\Controls\TextInput || $c instanceof Nette\Forms\Controls\SelectBox || $c instanceof Nette\Forms\Controls\SubmitButton
                    || $c instanceof Nette\Forms\Controls\Checkbox || $c instanceof Nette\Forms\Controls\CheckboxList}
                    {if $c->getName() == 'multiplier_creator'}
                    <div class="col-sm-12">
                        <br>
                        {elseif $c->getName() == 'multiplier_remover'}
                        <div class="col-sm-3 subject-header-input">
                            <br>
                            {else}
                            <div class="col-sm-3 subject-header-input">
                                {/if}
                                {label $c /}{input $c}
                            </div>

                            {if $c->getName() == 'multiplier_remover'}
                        </div>
                        <div class="col-sm-12">
                            {/if}

                            {if $c->getName() == 'dummy_format'}
                        </div>
                        <div class="col-sm-12">
                            {/if}
                    {/if}

                {/foreach}
            {else}
                {var $newGroup = $cntrl->getOption('group')}
                {if $cntrl instanceof Nette\Forms\Controls\TextInput || $cntrl instanceof Nette\Forms\Controls\SelectBox
                || $cntrl instanceof Nette\Forms\Controls\SubmitButton || $cntrl instanceof Nette\Forms\Controls\Checkbox
                || $cntrl instanceof Nette\Forms\Controls\CheckboxList}
                    {if $newGroup != $oldGroup}
                        <div class="col-md-12">
                            <br>
                            <h3>{$cntrl->getOption('group')}</h3>
                            <hr>
                        </div>
                        {var $oldGroup = $newGroup}
                    {/if}

                    <div class="col-md-3">
                        {label $cntrl /}{input $cntrl}
                    </div>
                {/if}

            {/if}

        {/foreach}
    {/snippet}
    </div>
{/form}

When i add new pair of attributes and select the item from first select, second select does not refresh. But when i add another pair of attributes, the second select of previous pair get the right values.

So it looks like it needs some kind of refresh (i tried add redraw of snippet after addCopy of the hack, but nothing happned).

MartkCz commented 7 years ago

In javascript

        function loadDependent() {
            $('[data-dependentselectbox]').dependentSelectBox();
        }
        loadDependent();
        $.nette.ext('foo', {
            success: function () {
                loadDependent();
            }
        });
BigOHenry commented 7 years ago

Man, you are amazing :-) its working like charm now :-) THANK YOU VERY MUCH!!!!!

BigOHenry commented 7 years ago

Hey, i have get one more problem. This time with default values. I always set default values for replicator with associative array:

$form['multiplier']->setValues(array( 1 => array( 'array' => 1, 'dependent_select' => 2 ), 2 => array( 'array' => 2, 'dependent_select' => 3 ) ));

It set the default values, but when i click on add button, i got this error: Component with name '2' already exists.

I dont know if its multiplier or dependent select box problem :(

MartkCz commented 7 years ago

Can you try dev-master?

BigOHenry commented 7 years ago

Its broken now:

1) without default values when i select item from first select: Form is not anchored and therefore can not determine whether it was submitted. at this line: $multiplier->addCopy($matches[1]);

2) with default values page load to this error: Component with name '1' does not exist.

MartkCz commented 7 years ago

Sorry, I forgot fill second parameter. https://github.com/WebChemistry/forms-multiplier/commit/8479c6496107b96d3c75c27998ee487d8dd2a33d

BigOHenry commented 7 years ago

Its still broken:

1. Without default values:

2. With default values:

MartkCz commented 7 years ago

Yeah, I see! https://github.com/WebChemistry/forms-multiplier/commit/87fea53d92c98f2cc85c5edb6c52b7c759e0d9c1 Thanks for all reports.

BigOHenry commented 7 years ago

Without default values it works fine now.

With default values its much better now, but it still adds extra pair of the second attributes pair (after add). But if i still adding new pairs, its not multiplied. screenshot here: https://ibb.co/et92Yk

MartkCz commented 7 years ago

Verry sorry, but I can't reproduce the error :(

BigOHenry commented 7 years ago

Yeah, thats was my issue :-) Thank you very much