Created by @Ages in Q&As
@Ages
@Ages

Hi, I have problem with dependent select box. I create method to find the product lines for selected producer, and this works well. But unfortunately then I return data it reset all of the fields in form. There is one possibility to send all of the fields in form and return them, but this in uncomfortable. Is there any other solution?

    {snippet myForm}
        {control productF}
    {/snippet}

<script>
window._stack.push([function (di, DOM) { DOM.addListener('frm-productF-producer', 'change', function (evt) {
                    var frm = di.getService('formLocator').getForm('frm-productF');
                    var page = di.getService('page');
                    var data = { producer: frm.getElement('producer').value};
page.open({link findProductLine!}, 'POST', { data: data},{ history: false, transition: false});
                });
            }, {DOM: 'Utils.DOM'}]);
</script>

Dohledání

    public function handleFindProductLine(array $data): void
    {
        if ($this->isAjax()) {
            $items = ['' => ''] + $this->orm->productLine->findBy(['producer->id' => $data['producer']])->fetchPairs('id', 'name');
            if ($items) {
                $this['productF']['productLine']->setItems($items);
            }
            $this->redrawControl('myForm');
        }
    }
@jahudka
@jahudka

Hi, sorry for the delay, you can prevent Nittro from resetting the form in a number of ways:

  • either you can globally turn off automatic form resetting via an option in the Nittro builder
  • or you can add data-reset="false" to the form element (this won't work in your case though, because you're dispatching the request manually as opposed to submitting the form and letting Nittro handle it - I'm just including this option for completeness' sake)
  • or you can call $this->disallowFormReset() just after $this->redrawControl('myForm') (this method is provided by the ComponentUtils trait from nittro/nette-bridges - you can use this trait in a base component, or you can use the PresenterUtils trait which extends it in a base presenter, or you can just have your base presenter extend the Presenter class provided by nittro/nette-bridges instead of the one from nette/application)

As a side note: I'm noticing you're calling frm.getElement(['producer']) in your script - I'm not sure if that will work, I think it should probably be frm.getElement('producer'), without the array.

@Ages
@Ages

@jahudka wrote:

Hi, sorry for the delay, you can prevent Nittro from resetting the form in a number of ways:

  • either you can globally turn off automatic form resetting via an option in the Nittro builder
  • or you can add data-reset="false" to the form element (this won't work in your case though, because you're dispatching the request manually as opposed to submitting the form and letting Nittro handle it - I'm just including this option for completeness' sake)
  • or you can call $this->disallowFormReset() just after $this->redrawControl('myForm') (this method is provided by the ComponentUtils trait from nittro/nette-bridges - you can use this trait in a base component, or you can use the PresenterUtils trait which extends it in a base presenter, or you can just have your base presenter extend the Presenter class provided by nittro/nette-bridges instead of the one from nette/application)

As a side note: I'm noticing you're calling frm.getElement(['producer']) in your script - I'm not sure if that will work, I think it should probably be frm.getElement('producer'), without the array.

Hi, thank you! But I tried to put on form element data-reset="false" and even add method $this->disallowFormReset() after the $this->redrawControl('myForm') but behavior is still the same. Field productLine has the new value and other fields has default values.

@jahudka
@jahudka

Oh, sorry, I forgot to mention: of course you must add a snippet which wraps the dependent select box (or any dependent form elements for that matter) and redraw only that snippet when the dependency changes - you're now redrawing the whole form and the backend has no way of knowing what values have been set in the other fields since you're not submitting those, so it will render a blank form, and since Nittro has no way of knowing if this is intentional or not it will simply leave that as is - you probably don't even need any of the solutions from my previous reply, just this!

@Ages
@Ages

@jahudka wrote:

Oh, sorry, I forgot to mention: of course you must add a snippet which wraps the dependent select box (or any dependent form elements for that matter) and redraw only that snippet when the dependency changes - you're now redrawing the whole form and the backend has no way of knowing what values have been set in the other fields since you're not submitting those, so it will render a blank form, and since Nittro has no way of knowing if this is intentional or not it will simply leave that as is - you probably don't even need any of the solutions from my previous reply, just this!

Ok, maybe I do something wrong because I try to put the snippet on productLine element and edit the presenter method like this:

    public function handleFindProductLine(array $data): void
    {
        if ($this->isAjax()) {
            $items = ['' => ''] + $this->orm->productLine->findBy(['producer->id' => $data['producer']])->fetchPairs('id', 'name');
            if ($items) {
                $this['productF']['productLine']->setItems($items);
            }
            $this->redrawControl('productLine');
            $this->disallowFormReset();
        }
    }

Method is called but there is no change on the item, do I need to put the form inside the snippetArea or something like that? Thank you

@jahudka
@jahudka

Well I'm not sure I follow your structure, but it looks like you have a ProductF component for the whole form - how do you render that? Because in the template excerpt you posted earlier it looks like you're rendering the whole form using {control productF} - so there wouldn't be any snippets within the form. You'd have to render the form manually and wrap the part which should change with the dependent select box in a snippet.

@Ages
@Ages

Yes, that's true! I try to add n:snippet attribute on productLine element. Is there any option to make it work with rendering by {control productF}? I'm sorry for stupid questions, but I would like to use this on several forms in app and I would like to use standard rendering. On different project I send all of the values to handleMethod and after some logic I set them as default values for form, this works but for the form with a lot of elements it is uncomfortable and also wasteful.

@jahudka
@jahudka

I'm afraid it's not possible - you can't add snippets to a form which you render using a renderer, you must render the form manually. Maybe it could be possible using a custom renderer, I've never delved too deep into those, but I'm confident this can't be done using the default renderer.

Sign in to post a reply