Ajax – překreslení formuláře nenačte data

d@rkWolf
Člen | 172
+
0
-

Zdravím,
mám add/edit formulář na adresu v komponentně – když odstraním ajax, vše funguje, data se uloží, redirect(this) reloadne stránku, formulář se načte s nově uloženými údaji, změní se popisek tlačítka (přidat->upravit).
Jak přidám třídu ajax, fungovat to přestane. Data se sice uloží, ale dotaz v ActionDefault, který má ty uložené údaje načíst a vyplnit je do formuláře vyjde s výsledkem null(ten bdump) – ten samý, který při redirectu funguje správně. Díky tomu nezmění tlačítko a nedoplní id záznamu, takže další úprava není možná. Je to u přihlášeného uživatele, getId() vrací ID správně, pokud si dotaz s výsledkem null skopíruju z Tracy do PhpMyAdmin, výsledkem je správně uložený řádek. Je tam použitá Naja 1.8×.
Úplně mi uniká, kde je problém.

Presenter:

final class MyPresenter extends BasePresenter
{
	use AddressFormFactory;

    public function actionDefault(int $itemId): void
    {
        $address = $this->webAddressRepo->getByUser($this->getUser()->getId());
		bdump($address);
        if ($address) {
            $addressForm =$this->getComponent('addressForm')['form'];
            $addressForm->addHidden('id');
            /** @var SubmitButton $send */
            $send = $addressForm->getComponent('send');
            $send->setCaption('Update address');
            $addressForm->setDefaults($address);
        }
    }
}

Traita s továrnou komponenty

trait AddressFormFactory
{
    #[Inject]
    public IAddressFormFactory $addressFormFactory;

    protected function createComponentAddressForm(): AddressForm
    {
        $control = $this->addressFormFactory->create($this->getUser()->getId());
        $control->onSave[] = function (AddressForm $control) {
            $control->flashMessage($this->translator->translate('Address saved'), 'alert alert-success');
            if ($this->isAjax()) {
                $control->redrawControl('form');
                $control->redrawControl('flashes');
            } else {
                $this->redirect('this');
            }
        };

        return $control;
    }
}

šablona komponenty s formulářem (použil jsem bootstrap formátování jako je v jednom z Examples)

{import '../../Presenters/templates/@components/form-bootstrap5.latte'}

<div n:snippet="flashes" data-naja-history-nocache>
    <div n:foreach="$flashes as $flash" n:class="alert, $flash->type, alert-dismissible, fade, show" role="alert">
        {$flash->message}
        <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
    </div>
</div>

<div class="account-form-area" n:snippet="form">
    {include bootstrap-form form}
</div>

v šabloně presenteru už je to pak vloženo jen takto:

{control addressForm}

Editoval d@rkWolf (8. 4. 2024 19:37)

nefilskyl
Člen | 2
+
+1
-

Odhadoval bych, že by to mohlo souviset s životním cyklem presenteru. Jako první se zpracuje action, potom handle. Jestli to chápu dobře, tak zpracování formuláře je vlastně implementováno jako signál – v každém formuláři se automaticky přidá hidden „_do“ s hodnotou „<formular>-submit“, který způsobí to, že presenter najde ten formulář v komponentách a invokuje příslušnou metodu signalReceived($signal), konkrétně tedy Nette\Application\UI\Form::signalReceived(‚submit‘), která zajistí zpracování formuláře.

To znamená, že se stane toto:

  1. spustí se actionDefault – ještě tam nic není uloženo, takže je výsledkem null
  2. spustí se obsluha signálu addressForm-form-submit – data se uloží
  3. renderDefault – tady už by data byla k dispozici, ale metoda se zde nepoužívá, takže se nic nestane
  4. překreslí se šablona – ale formulář nemá data, takže to vypadá jako že se nic neuložilo.

Při redirectu to funguje, protože po zpracování formuláře server vrátí 302, prohlížeč provede nový request a presenter se spustí celý znovu.

Řešení:

  • jako nejjednodušší úprava by asi stačilo přejmenovat actionDefault na renderDefault
  • lepší mi ale přijde získávat data a nastavit defaultní hodnoty přimo v komponentě s formulářem (formulář už má k dispozici id uživatele, není problém tam injectovat závislost webAddressRepo a data si načíst tam).

Editoval nefilskyl (24. 4. 9:33)

m.brecher
Generous Backer | 920
+
0
-

@darkWolf

mám add/edit formulář na adresu v komponentně – když odstraním ajax, vše funguje, data se uloží, redirect(this) reloadne stránku, formulář se načte s nově uloženými údaji, změní se popisek tlačítka (přidat->upravit).

Toto je běžný use-case, kdy formulář nemá data, uživatel je vyplní a odešle , form data uloží, db poskytne id nového záznamu a form přesměruje na url pro editaci, kam přidá nově získané $id. vždy by to mělo být redirect, protože je potřeba změnit url, url pro nový záznam i editaci by neměl být stejný – formulář v těchto režimech pracuje jinak, jinak tahá data apod…

Já ve všech handlerech formů – vesměs jsou všechny ajax-submitted, ale funguje to i bez ajaxu v create() (uložení nového záznamu) mám $this->redirect(‚update‘, [‚id‘ ⇒ $id]), kde $id získám z databáze – po uložení záznamu!

Nette umí redirect standardní non-ajax i ajaxový – je ale potřeba, aby ajax knihovna uměla s ajaxovým redirectem správně zacházet. Já Naja nepoužívám, mám vlastní AjaxHandler.js, kde ajaxový redirect vyvolá změnu url – to je smyslem redirectu. Naja to jistě také umí, ale možná se jí musí nějak pomoct.

V tvojí traitě s buildem formu vidím přesměrování $this->redirect(‚this‘) – toto by teoreticky nemělo vůbec fungovat, protože se dostaneš na url pro nový záznam, ale neznám detaily třeba tam to id nějak dostaneš.

Redraw formuláře je OK po editaci záznamu – zde se url nemění, pro nový záznam doporučuji přesměrovat na url pro editaci – v ajaxu i non-ajaxu!

Jinak při redraw platí, že bys měl načítat data do formu v render fázi !! ne action.

Zkus redirect() i pro ajax!