Redraw control – end() expects parameter 1 to be array, null given

cafesk8
Člen | 103
+
0
-

Zdravím,

mám v multi-step formuláři (webchemistry/wizard) na druhém kroku text input, dle jehož hodnoty potřebuji nastavit hodnoty v selectboxu. V js volám na onChange hadnle v presenteru, který mi má hodnoty selectboxu nastavit, při redrawControl však dojde k chybě end() expects parameter 1 to be array, null given

mám presenter:

final class MultiPresenter extends BasePresenter {

    /** @var Wizard */
    private $wizard;

    public function __construct(Wizard $wizard) {
        $this->wizard = $wizard;
    }

    public function handleChangeStep($step): void {
        $this->getComponent("wizard")->setStep($step);
    }

    protected function createComponentWizard(): Wizard {
       return $this->wizard;
    }

    public function handleZip($cislo) {

        $hodnoty_do_selectu = $vypocet_z_cislo;

        $formular = $this->getComponent('wizard')->getComponent('step2');
        $formular['object_city']->setItems($hodnoty_do_selectu);

        if ($this->isAjax()) {
            $this->redrawControl('psc_wrapper');
            $this->redrawControl('psc');
        } else {
            $this->redirect('this');
        }

    }
}

šablonu:

{block content}
    <div n:wizard="wizard" class="form-navigation">
        <ul n:if="!$wizard->isSuccess()" class="nav nav-pills nav-justified">
            <li n:foreach="$wizard->steps as $step" class="nav-item">
                ...
            </li>
        </ul>
        {step 1}
            {form $form}
                ....
            {/form}
        {/step}

        {step 2}
            {form $form}
                {snippetArea psc_wrapper}
                    {input object_type}
					{input object_zip}
                    {snippet psc}
						{input object_city}
                    {/snippet}
                    {input ins_value}
                    {input phone}
                    {input prev}
                    {input next}
                {/snippetArea}
            {/form}
        {/step}

        {step 3}
            {form $form}
                ....
            {/form}
        {/step}

        {step success}
           ....
        {/step}
    </div>
{/block}

{block scripts}
        <script n:syntax="double">
            var psc_link = {{link zip!}};
        </script>
    {include parent}
{/block}

a js:

$('.zip_field').each(function(){
    var $parent = $(this).closest('form');
    $(this).change(function (e) {
        var psc = $(this).val();
        $.nette.ajax({
            type: 'POST',
            url : $zip_url,
            data: {cislo: psc}
        });
        return false;
    });
});

Pokud dám snippet někam mimo formulář a zkouším do něj přes ajax posílat nějaké hodnoty nebo klidně i výsledek selectu, tak je to OK, pokud se však snažím překreslit část ve {form}{/form} – dojde k chybě. Jak jde vidět, tak jsem zkusil i obalit do {snippetArea} a invalidovat i ji, bohužel nepomohlo.

Zkoušel jsem obalit i celý form, ale to skončí chybou: Undefined variable: form

{snippetArea psc_wrapper}
	{form $form}
		....
		{snippet psc}
			{input object_city}
		{/snippet}
		...
	{/form}
{/snippetArea}

Když do snippetArea obalím celý {step}{/step}, tak mi vyskočí chyba: Undefined variable: wizard

Nesetkal jste se s tím někdo?

David Matějka
Moderator | 6445
+
0
-

obal cele to {step 2} do snippetArey

cafesk8
Člen | 103
+
0
-

Píši na konci dotazu, že když celý {step2} obalím do {snippetArea} tak mi stránka nenajede vůbec a háže to chybu: Undefined variable: wizard.

David Matějka napsal(a):

obal cele to {step 2} do snippetArey

Martk
Člen | 651
+
0
-

Měl bys celé n:wizard obalit, tam se vytváří proměnná $wizard

cafesk8
Člen | 103
+
0
-

To jsem také zkoušel, ale to mi skončí další chybou a to: Creating default object from empty value.
Source file: /vendor/latte/latte/src/Latte/Engine.php(161) : eval()'d code:122

Martk napsal(a):

Měl bys celé n:wizard obalit, tam se vytváří proměnná $wizard

cafesk8
Člen | 103
+
0
-

Co jsem googlil tu hlášku s „Creating default object from empty value.“ tak začínám mít podezření že to má co dočinění se Sessions, ale pořád jsem ve slepé uličce.

cafesk8
Člen | 103
+
0
-

@Martk : nic Vás nenapadá, nemáte nějaký funkční příklad překreslení snippetu v nějakém kroku?

děkuji

cafesk8 napsal(a):

To jsem také zkoušel, ale to mi skončí další chybou a to: Creating default object from empty value.
Source file: /vendor/latte/latte/src/Latte/Engine.php(161) : eval()'d code:122

Martk napsal(a):

Měl bys celé n:wizard obalit, tam se vytváří proměnná $wizard

Martk
Člen | 651
+
0
-

@cafesk8 Zkus dát proměnnou do šablony přes action metodu:

$this->template->wizard = new WebChemistry\Forms\Controls\Wizard\Facade($this['wizard']->getFacade());

nejlepší by bylo zjištění jaký kus kódu dělá problém.

Editoval Martk (7. 1. 2019 20:31)

cafesk8
Člen | 103
+
0
-

Zkusil jsem to, ale bylo mi řečeno že Wizard->getFacade() neexistuje. Udělal jsem nejjednodušší příklad z nette/sandbox:

HomepagePresenter.php

namespace App\Presenters;
use 	Wizard;

final class HomepagePresenter extends BasePresenter {
	public function renderDefault() {
		$this->template->anyVariable = 'any value';
	}

    /** @var Wizard */
    private $wizard;

    public function __construct(Wizard $wizard) {
        $this->wizard = $wizard;
    }

    public function handleChangeStep($step): void {
        $this->getComponent("wizard")->setStep($step);
    }

    protected function createComponentWizard(): Wizard {
        return $this->wizard;
    }

    public function handlePopulate($cislo) {

        $form = $this->getComponent('wizard')->getComponent('step2');
        $form['need_to_populate']->setItems(array('one' => 'ONE', 'two' => 'TWO'));

        if ($this->isAjax()) {
            $this->redrawControl('myArea');
            $this->redrawControl('mySnippet');
        } else {
            $this->redirect('this');
        }

    }
}

Wizard.php

use Nette\Application\UI\Form;

class Wizard extends WebChemistry\Forms\Controls\Wizard {

    protected function finish(): void {
        $values = $this->getValues();
    }

    protected function createStep1(): Form {
        $form = $this->createForm();

        $form->addText('name', 'User name')
            ->setRequired();

        $form->addSubmit(self::NEXT_SUBMIT_NAME, 'Next');

        return $form;
    }

    protected function createStep2(): Form {
        $form = $this->createForm();
        $form->getElementPrototype()->class = 'ajax';

        $form->addText('populate_on_change', 'Email')
            ->setRequired();

        $form->addSelect('need_to_populate','Naplnit',array());

        $form->addSubmit(self::PREV_SUBMIT_NAME, 'Back');
        $form->addSubmit(self::NEXT_SUBMIT_NAME, 'Next');

        return $form;
    }

    protected function createStep3(): Form {
        $form = $this->createForm();

        $form->addText('email', 'Email')
            ->setRequired();

        $form->addSubmit(self::PREV_SUBMIT_NAME, 'Back');
        $form->addSubmit(self::FINISH_SUBMIT_NAME, 'Register');

        return $form;
    }
}

Homepage/default.php

{block content}
    {snippetArea myArea}
        <div n:wizard="wizard">
            <ul n:if="!$wizard->isSuccess()">
                <li n:foreach="$wizard->steps as $step" n:class="$wizard->isDisabled($step) ? disabled, $wizard->isActive($step) ? active">
                    <a n:tag-if="$wizard->useLink($step)" n:href="changeStep! $step">{$step}</a>
                </li>
            </ul>

            {step 1}
                {control $form}
            {/step}

            {step 2}
                {form $form}
                    {input populate_on_change, class=>"watch_this"}
                    {snippet mySnippet}
                        {input need_to_populate}
                    {/snippet}
                    {input next}
                    {input prev}
                {/form}
            {/step}

            {step 3}
                {control $form}
            {/step}

            {step success}
                Registration was successful
            {/step}
        </div>
    {/snippetArea}
{/block}

{block scripts}
        <script n:syntax="double">
            var handle_link = {{link populate!}};
        </script>
    {include parent}
{/block}

main.js

$(function(){
	var $hadnle_url = handle_link;
	$('.watch_this').each(function(){
		$(this).change(function (e) {
			var cislo = $(this).val();
			    $.nette.ajax({
			        type: 'POST',
			        url : $hadnle_url,
			        data: {cislo: cislo}
			    });
			    return false;
		});
	});
});

Nyní, když si stránku v prohlížeči načtu, tak mám hlášku:
„Warning: Creating default object from empty value“
„Source file: vendor/latte/latte/src/Latte/Engine.php(161) : eval()'d code:58“

Call stack:

  1. …/vendor/latte/latte/src/Latte/Runtime/Template.php:282 source Template98aabccf63->blockMyArea(arguments)
  2. inner-code:49 Latte\Runtime\Template->renderBlock(arguments)
  3. …/vendor/latte/latte/src/Latte/Runtime/Template.php:282 source Template98aabccf63->blockContent(arguments)

V arguments bodu 2 je:
$name „_myArea“
$params array(..)

Pokud dám z šablony pryč snippetArea tak se formulář vykreslí ale při změně inputu vyskočí hláška: Redraw control – end() expects parameter 1 to be array, null given

Soubor s daty kdyžtak zde

Martk napsal(a):

@cafesk8 Zkus dát proměnnou do šablony přes action metodu:

$this->template->wizard = new WebChemistry\Forms\Controls\Wizard\Facade($this['wizard']->getFacade());

nejlepší by bylo zjištění jaký kus kódu dělá problém.

Editoval cafesk8 (8. 1. 2019 11:00)

Martk
Člen | 651
+
0
-

To getFacade tam být nemělo. Podívám se na ukázku.

Martk
Člen | 651
+
0
-

Tvoje ukázka mi v sandboxu fungovala:

		"php": ">=7.2.0",
		"nette/application": "^2.4.4",
		"nette/bootstrap": "^2.4.3",
		"nette/caching": "^2.5",
		"nette/database": "^2.4",
		"nette/di": "^2.4",
		"nette/finder": "^2.4",
		"nette/forms": "^2.4",
		"nette/http": "^2.4",
		"nette/mail": "^2.4",
		"nette/robot-loader": "^2.4 || ^3.0",
		"nette/safe-stream": "^2.3",
		"nette/security": "^2.4",
		"nette/utils": "^2.4",
		"latte/latte": "^2.4",
		"tracy/tracy": "^2.4",
		"dg/adminer-custom": "^1.9",
		"webchemistry/forms-wizard": "dev-master"

PHP 7.3.0, latte: v2.4.8

cafesk8
Člen | 103
+
0
-

@Martk : Bohužel, mně ne, ale jedu na PHP 7.1, zkusím upgrade na 7.3 a uvidím, ale ty verze mám stejný jako ty :/ Dám vědět.

Martk napsal(a):

Tvoje ukázka mi v sandboxu fungovala:

		"php": ">=7.2.0",
		"nette/application": "^2.4.4",
		"nette/bootstrap": "^2.4.3",
		"nette/caching": "^2.5",
		"nette/database": "^2.4",
		"nette/di": "^2.4",
		"nette/finder": "^2.4",
		"nette/forms": "^2.4",
		"nette/http": "^2.4",
		"nette/mail": "^2.4",
		"nette/robot-loader": "^2.4 || ^3.0",
		"nette/safe-stream": "^2.3",
		"nette/security": "^2.4",
		"nette/utils": "^2.4",
		"latte/latte": "^2.4",
		"tracy/tracy": "^2.4",
		"dg/adminer-custom": "^1.9",
		"webchemistry/forms-wizard": "dev-master"

PHP 7.3.0, latte: v2.4.8

cafesk8
Člen | 103
+
0
-

@Martk

Žádný úspěch ani na PHP 7.3

composer.json

{
	"name": "nette/sandbox",
	"description": "The sandbox is a pre-packaged Nette Framework project, basic configured structure for your application.",
	"homepage": "https://nette.org",
	"type": "project",
	"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
	"authors": [
		{
			"name": "David Grudl",
			"homepage": "https://davidgrudl.com"
		},
		{
			"name": "Nette Community",
			"homepage": "https://nette.org/en/contributors?lang=en"
		}
	],
	"require": {
		"php": ">=7.2.0",
		"nette/application": "^2.4.4",
		"nette/bootstrap": "^2.4.3",
		"nette/caching": "^2.5",
		"nette/database": "^2.4",
		"nette/di": "^2.4",
		"nette/finder": "^2.4",
		"nette/forms": "^2.4",
		"nette/http": "^2.4",
		"nette/mail": "^2.4",
		"nette/robot-loader": "^2.4 || ^3.0",
		"nette/safe-stream": "^2.3",
		"nette/security": "^2.4",
		"nette/utils": "^2.4",
		"latte/latte": "^2.4",
		"tracy/tracy": "^2.4",
		"dg/adminer-custom": "^1.9",
		"webchemistry/forms-wizard": "dev-master"
	},
	"require-dev": {
		"nette/tester": "^2.0"
	},
	"minimum-stability": "stable",
	"config": {
		"platform": {
			"php": "7.3"
		}
	}
}

Když dám pryč snippetArea tak se web normálně rozběhne, ale při změně inputu mi vyskočí hláška „end() expects parameter 1 to be array, null given“. Když tam snippetArea nechám, tak mni stránka ani nenajede a mám tam pořád chybu „Creating default object from empty value“. Nenapadá Vás ještě něco? Případně je nějaká možnost se spojit?

cafesk8 napsal(a):

@Martk : Bohužel, mně ne, ale jedu na PHP 7.1, zkusím upgrade na 7.3 a uvidím, ale ty verze mám stejný jako ty :/ Dám vědět.

Martk napsal(a):

Tvoje ukázka mi v sandboxu fungovala:

		"php": ">=7.2.0",
		"nette/application": "^2.4.4",
		"nette/bootstrap": "^2.4.3",
		"nette/caching": "^2.5",
		"nette/database": "^2.4",
		"nette/di": "^2.4",
		"nette/finder": "^2.4",
		"nette/forms": "^2.4",
		"nette/http": "^2.4",
		"nette/mail": "^2.4",
		"nette/robot-loader": "^2.4 || ^3.0",
		"nette/safe-stream": "^2.3",
		"nette/security": "^2.4",
		"nette/utils": "^2.4",
		"latte/latte": "^2.4",
		"tracy/tracy": "^2.4",
		"dg/adminer-custom": "^1.9",
		"webchemistry/forms-wizard": "dev-master"

PHP 7.3.0, latte: v2.4.8

Martk
Člen | 651
+
0
-

Poslal jsem jeden commit na git, možná to bylo tím. Jestli ne, tak jsem na https://pehapkari.slack.com pod stejným nickem jako tady.

PS: je potřeba vymazat latte cache

Editoval Martk (9. 1. 2019 15:37)

cafesk8
Člen | 103
+
0
-

@Martk: Super! Pomohlo to, díky moc!

Martk napsal(a):

Poslal jsem jeden commit na git, možná to bylo tím. Jestli ne, tak jsem na https://pehapkari.slack.com pod stejným nickem jako tady.

PS: je potřeba vymazat latte cache

cafesk8
Člen | 103
+
0
-

@Martk: Hodnoty se mi do selectu naplní v pořádku, problém nastane ve chvíli, kdy pokračuji na další krok. Hodnota se neuloží, tudíž s ní nemůžu dále pracovat. Při getValues() je výsledek tohoto pole vždy null.

Potřeboval bych totiž v dalším kroku dle hodnoty, kterou někdo v předchozím selectu vybral naplnit další select.

Editoval cafesk8 (10. 1. 2019 16:47)

Martk
Člen | 651
+
0
-

@cafesk8 Je to tím, že nette porovnává odeslanou hodnotu s tím, co je v $items v selectboxu. V této době je $items === [] . Kdyby to nedělal, tak by uživatel mohl podstrčit jakoukoliv vlastní hodnotu.

Zkus udělat tohle ve formuláři:

		$form->onAnchor[] = function (Form $form): void {
			if (!$form->isSubmitted()) {
				return;
			}
			$populateOnChange = $form->getValues(true)['populate_on_change'];

			$form['need_to_populate']->setItems([
				'one' => 'ONE',
				'two' => 'TWO',
			]);
		};
cafesk8
Člen | 103
+
0
-

@Martk Super, tohle funguje.

Dle hodnoty v radio listu v kroku č. 1 naplním v kroku č. 2 select, mohu přecházet tam a zpátky a vše funguje OK.
Co se mi však nedaří je, že když se vrátím zpět na krok č. 1 a zvolím v radio listu jinou možnost než zvolenou původně, tak se mi již nepodaří dostat na krok č. 2, kvůli právě ochranně vkládání polí.

Value ‚one_a‘ is out of allowed set [‚one_b‘, ‚two_b‘] in field ‚need_to_populate‘.

Tudíž bych po změně v kroku č. 1 potřeboval jakoby vyresetovat hodnotu z kroku č. 2, něco jako

function createStep2() {
	if($step1['radiolist'] !== $formvalues['radiolist']) {
		// vyresetovat hodnotu z prvního kroku abych mohl naplnit select v kroku č. 2
	}
}

Ráno zkusím kdyžtak sepsat příklad jak to mám teď.

Martk napsal(a):

@cafesk8 Je to tím, že nette porovnává odeslanou hodnotu s tím, co je v $items v selectboxu. V této době je $items === [] . Kdyby to nedělal, tak by uživatel mohl podstrčit jakoukoliv vlastní hodnotu.

Zkus udělat tohle ve formuláři:

		$form->onAnchor[] = function (Form $form): void {
			if (!$form->isSubmitted()) {
				return;
			}
			$populateOnChange = $form->getValues(true)['populate_on_change'];

			$form['need_to_populate']->setItems([
				'one' => 'ONE',
				'two' => 'TWO',
			]);
		};

Editoval cafesk8 (15. 1. 2019 17:05)

cafesk8
Člen | 103
+
0
-

Dočasně jsem vyřešil přes: ->checkAllowedValues = false

cafesk8 napsal(a):

@Martk Super, tohle funguje.

Dle hodnoty v radio listu v kroku č. 1 naplním v kroku č. 2 select, mohu přecházet tam a zpátky a vše funguje OK.
Co se mi však nedaří je, že když se vrátím zpět na krok č. 1 a zvolím v radio listu jinou možnost než zvolenou původně, tak se mi již nepodaří dostat na krok č. 2, kvůli právě ochranně vkládání polí.

Value ‚one_a‘ is out of allowed set [‚one_b‘, ‚two_b‘] in field ‚need_to_populate‘.

Tudíž bych po změně v kroku č. 1 potřeboval jakoby vyresetovat hodnotu z kroku č. 2, něco jako

function createStep2() {
	if($step1['radiolist'] !== $formvalues['radiolist']) {
		// vyresetovat hodnotu z prvního kroku abych mohl naplnit select v kroku č. 2
	}
}

Ráno zkusím kdyžtak sepsat příklad jak to mám teď.

Martk napsal(a):

@cafesk8 Je to tím, že nette porovnává odeslanou hodnotu s tím, co je v $items v selectboxu. V této době je $items === [] . Kdyby to nedělal, tak by uživatel mohl podstrčit jakoukoliv vlastní hodnotu.

Zkus udělat tohle ve formuláři:

		$form->onAnchor[] = function (Form $form): void {
			if (!$form->isSubmitted()) {
				return;
			}
			$populateOnChange = $form->getValues(true)['populate_on_change'];

			$form['need_to_populate']->setItems([
				'one' => 'ONE',
				'two' => 'TWO',
			]);
		};