Jak v nette zprovoznit odeslání formuláře pomocí ajaxu

andros
Člen | 145
+
0
-

Ahoj, mohl by mi prosím, někdo poradit funkční příklad odeslání formuláře ajaxem ? Rád bych si to vyzkoušel na první aplikaci. Našel jsem tento návod: https://pla.nette.org/…ivajici-ajax
Tento návod využívá js soubor jquery.ajaxform.js (https://componette.org/search/?…) , odkaz ale na tento soubor již neukazuje.

petr.jirous
Člen | 128
+
+1
-

použij https://componette.org/…tte.ajax.js/ a pak už jenom stačí formuláři dát třídu ajax

andros
Člen | 145
+
0
-

Nějak se mi nedaří přemluvit formulář, aby fungoval ajaxově. Co potřebuju: v detailu produktu mám formulář pro vložení zboží do košíku. Bez ajaxu vše funguje.

v šabloně mám nalinkovaný soubor nette.ajax.js

<script type="text/javascript" src="{$basePath}/js/nette.ajax.js"></script>

v js souboru mám inicializaci:

$(function () {
    $.nette.init();
});

V šabloně presenteru potřebuju pro začátek znovu vykreslit jet flash zprávu, takže jsem ji obalil do snipetu:

{snippet flash}
    <div class="row">
        <div class="col-lg-12">
            {foreach $flashes as $flash}
                <div class="alert alert-dismissible alert-{$flash->type}">
                    <button type="button" class="close" data-dismiss="alert">&times;</button>
                    {$flash->message}
                </div>
            {/foreach}
        </div>
    </div>
    {/snippet}

a v presenteru mám metody pro formulář:

protected function createComponentAddToBasketForm()
    {
        $form = new Form;
        $form->getElementPrototype()->class('ajax');
        $form->addText('quantity', 'Počet ks:');

        $form->addHidden('prod_id');
        $form->addHidden('guest_id');
        $form->addHidden('name');
        $form->addHidden('price');

        $form->setDefaults([
            'quantity' => 1,
            'prod_id' => $this->product['id'],
            'name' => $this->product['name'],
            'price' => $this->product['d1']

        ]);

        $form->addSubmit('send', 'Do košíku');

        $form->onSuccess[] = [$this, 'AddToBasketFormSucceeded'];
        return $form;
    }

    public function AddToBasketFormSucceeded($form, $values)
    {

        $this->productManager->insertProduct([
            'quantity' => $values->quantity,
            'prod_id' => $values->prod_id,
            'guest_id' => $this->getHttpRequest()->getCookie('guest'),
            'name' => $values->name,
            'price' => $values->price
        ]);

        $this->flashMessage('Přidali jste zboží do košíku', 'success');

        if (!$this->isAjax())
            $this->redirect('Product:default', array("id" => $values->prod_id));
        else {

            $this->redrawControl('flash');
        }

    }

Po odeslání formuláře se mi vůbec neprovede zpracování formuláře ( vložení dat do databáze) a flash zpráva se nezobrazí.
Laděnka říká že tam něco ajaxového proběhlo
http://prntscr.com/e036ox

Ale provede se to samé, jako při načtení stránky.

Kde dělám chybu ?

Pokud odstraním z formuláře class ajax vše funguje jak má

iNviNho
Člen | 352
+
0
-

Skús pozrieť v chrome cez developer consolu network, či naozaj po stlačení submit tlačídla sa posiela niečo na server.

Mal by si tam vidieť tvoj link a za nim niečo ako URL&do=addToBasketForm…

Taktiež ešte skontroluj či po submitnutí ti nevyhodí v console nejaký error.

Editoval iNviNho (25. 1. 2017 12:56)

andros
Člen | 145
+
0
-

class ajax přidávám v továrničce formuláře:

$form->getElementPrototype()->class('ajax');

pokud ho přidám přímo do formuláře přímo na button, udělá se klasický redirect

Konsole žádnou chybu nevyhodí
v Network se po kliknutí pošle na server url adresa aktuální stránky, bez dalších parametrů, takže jako kdyby se stránka načetla znova

CZechBoY
Člen | 3608
+
0
-

Kde si ukládáš ten $this->product? V konstruktoru nebo až někdy pozdě (render metody)?

andros
Člen | 145
+
0
-

hned na začátku renderDefault mám:

$this->product = $this->template->product = $this->productManager->getProduct()
CZechBoY
Člen | 3608
+
-1
-

Včera/dneska jsem psal, že je potřeba to mít v action metodě :-) prácí s šablonou samozřejmě nechej v render metodě.

private $product;

public function actionDefault()
{
	$this->product = $this->productManager->getProduct();
}

public function renderDefault()
{
	$this->template->product = $this->product;
}
andros
Člen | 145
+
0
-

I když to přesunu do metody actionDefault, problém je pořád stejný
Celý presenter vypadá takto:

<?php

namespace FrontModule;
use Nette\Application\UI\Form;
use App\Model\CategoryManager;
use App\Model\ProductManager;

class ProductPresenter extends BasePresenter
{
    private $productManager;
    private $categoryManager;
    private $product;

    /**
     * @var $categoryId
     *
     */
    private $categoryId;

    private $categories;

    public function __construct(ProductManager $productManager, CategoryManager $categoryManager )
    {
        parent::__construct();
        $this->productManager = $productManager;
        $this->categoryManager = $categoryManager;
    }

    public function renderDefault($id)
    {

        $this->template->product = $this->product;

        /**
         * ID defaultní kategorie produktu (pokud neexistuje defaultní kategorie , použije se první nalezená
         */

        $this->template->categoriesBreadcrumb = $this->categories;

        $this->template->stock = $this->product->ref('stock', 'stock_id')->name;
        $this->template->stock_label = $this->product->ref('stock', 'stock_id')->label_type;
        $this->template->producer = $this->product->ref('prod_producer', 'producer_id')->name;
        $this->template->images = $this->product->related('prod_images');
        $this->template->image_default = $this->product->related('prod_images')->where('default_img', '1')->fetch();

    }

    public function actionDefault($id)
    {
        $this->product = $this->productManager->getProduct()
            ->where('active',1)
            ->get($id);

        if (!$this->product) {
            $this->error('Produkt nebyl nalezen!');
        }

        $this->categoryId = $this->product->related('prod_cat')->where('default','1')->fetch();

        if (!$this->categoryId) {
            $this->categoryId = $this->product->related('prod_cat')->limit(1)->fetch();
        }

        $this->categories = $this->getCategoriesParentName($this->categoryId->category_id);
        krsort($this->categories);
    }

    protected function createComponentAddToBasketForm()
    {
        $form = new Form;
        $form->getElementPrototype()->class('ajax');
        $form->addText('quantity', 'Počet ks:');

        $form->addHidden('prod_id');
        $form->addHidden('guest_id');
        $form->addHidden('name');
        $form->addHidden('price');


        $form->setDefaults([
            'quantity' => 1,
            'prod_id' => $this->product['id'],
            'name' => $this->product['name'],
            'price' => $this->product['d1']

        ]);

        $form->addSubmit('send', 'Do košíku');

        $form->onSuccess[] = [$this, 'AddToBasketFormSucceeded'];
        return $form;
    }

    public function AddToBasketFormSucceeded($form, $values)
    {

        $this->productManager->insertProduct([
            'quantity' => $values->quantity,
            'prod_id' => $values->prod_id,
            'guest_id' => $this->getHttpRequest()->getCookie('guest'),
            'name' => $values->name,
            'price' => $values->price
        ]);

        $this->flashMessage('Přidali jste zboží do košíku', 'success');

        if (!$this->isAjax())
            $this->redirect('Product:default', array("id" => $values->prod_id));
        else {

            $this->redrawControl('flash');
        }

    }

}
alNath
Člen | 17
+
0
-

Nestacilo by ti?

$form->onSubmit[]

namiesto

$form->onSuccess[]

andros napsal(a):

hned na začátku renderDefault mám:

$this->product = $this->template->product = $this->productManager->getProduct()

Editoval alNath (25. 1. 2017 13:50)

andros
Člen | 145
+
0
-

Vycházím z návodu na první aplikaci (v dokumentaci).
i když to ale změním na

$form->onSuccess[] = [$this, 'AddToBasketFormSucceeded'];

problém to nevyřeší.

andros
Člen | 145
+
0
-

oprava:

$form->onSubmit[] = [$this, 'AddToBasketFormSucceeded'];

je uplně jedno jetli tam je onSubmit nebo onSuccess.

Editoval andros (25. 1. 2017 13:49)

CZechBoY
Člen | 3608
+
0
-

Když odešleš formulář tak se vytvoří POST požadavek? Pošli jaké parametry tam jsou a jaká je odpověď.

andros
Člen | 145
+
0
-

Tak s tím bych potřeboval trošku pomoc, kde najdu , co potřebuješ poslat ?

CZechBoY
Člen | 3608
+
0
-
  1. Nástroje pro vývojáře (třeba přes pravej klik někam do stránky, poslední položka Revize prvku)
  2. záložka Network/Síť
  3. vyfiltruj XHR
  4. klikni na tlačítko ve formuláři
  5. měl by se ti tam objevit jeden http požadavek, klikni na něj
  6. zkopíruj http hlavičky i parametry pro požadavek i odpověď
  7. přepni na záložku Response/Odpověď a pak sem napiš jestli to je json (pošli) nebo html
andros
Člen | 145
+
0
-

Takže:

Request URL:http://localhost/playhracky3/www/product/10230
Request Method:GET
Status Code:200 OK
Remote Address:127.0.0.1:80
Response Headers
view source
Cache-Control:no-store, no-cache, must-revalidate
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:6645
Content-Type:text/html; charset=utf-8
Date:Wed, 25 Jan 2017 13:29:31 GMT
Expires:Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive:timeout=5, max=100
Pragma:no-cache
Server:Apache/2.2.31 (Win32) DAV/2 mod_ssl/2.2.31 OpenSSL/1.0.2e mod_fcgid/2.3.9 mod_wsgi/3.4 Python/2.7.6 PHP/7.0.9 mod_perl/2.0.8 Perl/v5.16.3
Set-Cookie:PHPSESSID=9dtm5fd55auqrscdlt212edqm0; expires=Wed, 08-Feb-2017 13:29:31 GMT; Max-Age=1209600; path=/; HttpOnly
Vary:X-Requested-With,Accept-Encoding
X-Frame-Options:SAMEORIGIN
X-Powered-By:Nette Framework
X-Tracy-Ajax:1
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:cs-CZ,cs;q=0.8
Connection:keep-alive
Cookie:Phpstorm-f1db6934=37031b54-37ac-4adb-97c5-b4368090fd7c; guest=58813b0babe94; PHPSESSID=9dtm5fd55auqrscdlt212edqm0
Host:localhost
Referer:http://localhost/playhracky3/www/product/10230
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
X-FireLogger:1.3
X-Requested-With:XMLHttpRequest
X-Tracy-Ajax:59591ff4e7

V záložce response je html celé stránky.

andros
Člen | 145
+
0
-

připadá mi, že je to podobný problém jako tady: https://forum.nette.org/…lar-s-ajaxem

andros
Člen | 145
+
0
-

co nechápu je, že když ve formuláři místo

<button class="btn btn-primary">
  <i class="fa fa-shopping-cart"></i>
  <span>Do košíku</span>
</button>

dám

<input n:name=send class="btn btn-primary" value="do košíku" />

tak to opět udělá klasický redirect

andros
Člen | 145
+
0
-

zkusil jsem místo formuláře v šabloně vypsat formulář

{control addToBasket}

a konzole vyhodi chybu:

Uncaught ReferenceError: Nette is not defined
at Object.before (nette.ajax.js:318)
at Function.<anonymous> (nette.ajax.js:42)
at Function.each (jquery.js:371)
at Object.fire (nette.ajax.js:40)
at Object.settings.beforeSend (nette.ajax.js:215)
at Function.ajax (jquery.js:8614)
at nette.ajax (nette.ajax.js:225)
at HTMLInputElement.requestHandler (nette.ajax.js:48)
at HTMLFormElement.dispatch (jquery.js:4737)
at HTMLFormElement.elemData.handle (jquery.js:4549)

andros
Člen | 145
+
+1
-

Tak po mnoha pokusech pokus – omyl jsem nakonec se mi to nakonec povedlo rozhýbat :)

Chyby jsem tam měl dvě
ve formuláři jsem měl

<button class="btn btn-primary">
</button>

Ono to chce ale

<button n:name=send class="btn btn-primary">
</button>

a nenačítal jsem scrip netteForms.min.js bez kterého se to neobejde. Protože ve formuláři nemám validaci, mysel jsem že je tento script zbytečný. Ovšem nette.ajax.js se bez něj prostě neobejde

andros
Člen | 145
+
0
-

Když už mi funguje odesílání formuláře ajaxem, snažím se přidat spinner, aby bylo vidět, že se něco děje, než se požadavek zpracuje.

v @layout.latte jsem přidal před inicializaci $.nette.init();

<script src="{$basePath}/js/extensions/spinner.ajax.js"></script>

do css jsem dal

#ajax-spinner {
    position: fixed;
    width: 32px;
    height: 32px;
    /* - use if you use bacground image, no ico */
    background: url('../img/ajax-loader.gif') no-repeat;
    z-index: 123456;
    color: green;
}

Ale spinner se ne a ne objevit :)

Když to spinner.ajax.js přidám na zkoušku alert (např.)

start: function () {
            this.counter++;
            if (this.counter === 1) {
                this.spinner.show(this.speed);
                alert('test');
            }
        },

Tak po kliknutí na tlačítko formuláře se mi alert okno zobrazí, takže by to mělo fungovat. Ale nastylovaný spinner se nezobrazí.

Poradíte mi prosím někdo ?

novasol
Člen | 1
+
+1
-

Jen doplňující informaci, snad to někomu pomůže. Po pěti hodinách jsem zjistil, že hned pod namespace v presenteru používám

use Nette\Forms\Form;

namísto správného

use Nette\Application\UI\Form;

Dejte si na to pozor! Jinak Vám AJAX pojede, ale nebude se volat callback v presenteru a pokaždé bude response komplet stránka, resp. funkce renderDefault() !

Editoval novasol (17. 5. 2018 21:24)