Dependency select Ajax a ztráta event handleru

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

Ahoj

Řeším jednoduchý problém s dynamickým načítáním seznamu krajů a okresů v závislosti na státu. Mám definovaný snippet pro překreslení selectu s kraji a okresy (v závislosti na státu) a snippet pro překlesení selectu s okresy (v závislosti na kraji). Při prvním načtení stránky se JS event listenery na selecty zaregistrují. Při výběru státu a překreslení selectu s kraji, se však handler selectu s výběrem kraje zruší a druhý (vnořený) snippet tak vůbec nefunguje. Mám za to, že je třeba listener onchange události na element registrovat znovu, což mě ale trochu mate, poněvadž když jsem při výběru státu select s okresy nepřekresloval, a tudíž jsem nepotřeboval užívat vnořeného snippetu (v kódu byly dva odděleně), listener se nerušil. Dokážete mi poradit, co s tím? Děkuju.

Šablona:

<form n:name=companyForm>
<div class="form-group form-group-sm">
					<div class="col-sm-3 control-label"><label n:name="state">Stát</label></div>
					<div class="col-sm-9"><select n:name="state"></select></div>
				</div>
{snippet stateSnippet}
				<div class="form-group form-group-sm">
					<div class="col-sm-3 control-label"><label n:name="region">Kraj</label></div>
					<div class="col-sm-9"><select n:name="region"></select></div>
				</div>
{snippet regionSnippet}
				<div class="form-group form-group-sm">
					<div class="col-sm-3 control-label"><label n:name="okres">Okres</label></div>
					<div class="col-sm-9"><select n:name="okres"></select></div>
				</div>
				{/snippet}
{/snippet}
	<p class="text-right"><input n:name=submit></p>
</form>

<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
</script>
$(function () {
    $.nette.init();
});
        </script>

        <script>{include #jsCallback, input => state, link => stateChange}</script>
        <script>{include #jsCallback, input => region, link => regionChange}</script>

{define #jsCallback}

$('#{$control["companyForm"][$input]->htmlId}').on('change', function() {
    $.nette.ajax({
        type: 'GET',
        url: '{link {$link}!}',
        data: {
            'value': $(this).val(),
        }
    });
});

{/define}

Presenter:

namespace App\AdminModule\Presenters;

use Nette\Application\UI;
use Nette\Utils\Finder;
use Nette\Forms\Form;
use Nette\Forms\Controls;
use Nette\Utils\Html;

class CompanyPresenter extends BasePresenter
{

	protected function beforeRender()
	{
		parent::beforeRender();
		$this->template->companyForm = $this['companyForm'];
		$this->template->_form = $this['companyForm'];
	}

	public function createComponentCompanyForm()
	{
		$formCompany = new \Nette\Application\UI\Form();

		$states = $this->database->table("state")->fetchAssoc("id=title");
		$SelectState = $formCompany->addSelect("state", "Stát", $states)->setDefaultValue(37);
		$regions = $this->database->table("region")->where("state=?", 37)->fetchAssoc("id=title");
		$SelectRegion = $formCompany->addSelect("region", "Kraj", $regions)->setDisabled(count($regions) == 0);
		$okresy = count($regions) ? $this->database->table("okres")->where("region=?", $regions[0])->fetchAssoc("id=title") : array();
		$SelectOkres = $formCompany->addSelect("okres", "Okres", $okresy)->setDisabled(count($okresy) == 0);

	$formCompany->addSubmit("submit", "Odeslat");
		$formCompany->onSuccess[] = $this->companyFormSucceeded;
		return $formCompany;
	}

	public function handleStateChange($value)
	{
		if ($value)
		{
			$data = $this->database->table("region")->where("state=?", $value)->fetchAssoc("id=title");

		}
		else
		{
			$data = array();
		}

		$this['companyForm']['region']->setItems($data)->setDisabled(count($data) == 0);
		$this['companyForm']['okres']->setItems(array())->setDisabled(true);
		$this->invalidateControl('stateSnippet');
	}

	public function handleRegionChange($value)
	{
		if ($value)
		{
			$data = $this->database->table("okres")->where("region=?", $value)->fetchAssoc("id=title");
		}
		else
		{
			$data = array();
		}
		$this['companyForm']['okres']->setItems($data)->setDisabled(count($data) == 0);
		$this->invalidateControl('regionSnippet');
	}

}
kleinpetr
Člen | 480
+
0
-

Pokud ti po načtení stránky vše funguje a po překlreslení ne, tak budeš muset listener zaregistrovat znovu po překreslení snippetu, já to dělám takhle, pomocí ajax rozšíření:

$.nette.ext('ajax', {
        success: function (payload) {

            for (snippet in payload.snippets) {

            //reinitialize listeners
	    	$('#'+snippet+' #{$control["companyForm"][$input]->htmlId}').on('change',function() {
    			$.nette.ajax({
        			type: 'GET',
        			url: '{link {$link}!}',
        			data: {
            			'value': $(this).val(),
        			}
    			});
			});

            }
        }
    });

Nebo můžeš samozřejmě tou horší variantou a to tak, že si dáš ten js listener do snippetu, který také budeš překreslovat.

Plus používej redrawControl() místo invalidateControl()

P.S. Tak i tak ti to neprojde přes onSuccess() funkci formuláře, jelikož budeš měnit data ajaxově, takže select bude obsahovat data, která tam při načtení nebyla. Takže až narazíš na tento problém, tak použij onSubmit()

Editoval kleinpetr (4. 3. 2016 11:17)

CZechBoY
Člen | 3608
+
0
-

btw lepší řešení rebindu po načtení snippetů je (podle mých nových poznatků :D)

$.nette.ext('myNewAjaxExtension', {
	init: function () {
		this.ext('snippets').after(function ($snippet) {
			bindLoaders($snippet);
		});
    }
});

Editoval CZechBoY (4. 3. 2016 11:55)

Daewoo
Člen | 37
+
0
-

Listenery fungují, ale onSubmit, onClick na submit button ani onSuccess však ne zcela.

$values = $Form->getValues();

Toto nevrací data od překreslených prvků, které byly předtím disabled. Data získaná přímo prostřednictvím _POST jsou však v pořádku, AJAX tedy funguje jak má.

kleinpetr
Člen | 480
+
0
-

@CZechBoY můžeš trochu rozvést ten rebind ? Díky :)

CZechBoY
Člen | 3608
+
0
-

@kleinpetr asi takhle nějak:

function bindLoaders($snippet)
{
	$('select', $snippet).on('change', function() {
        $.nette.ajax({
            type: 'GET',
            url: $(this).data('url'),
            data: {
               'value': $(this).val(),
            }
        });
    };

Šlo mi hlavně o změnu ze success (která se volá s každým requestem) metody na init (která se volá jen jednou).

Editoval CZechBoY (18. 10. 2016 13:22)