Zajaxování divu, buttonu, jak na to?

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

Ahoj,
pokouším se zAJAXovat jednu komponentu, respektive výpis z ní. Je to selectBox který vypíše seznam článků v dané kategorii. Komponenta je vytvořena přes interface, ale to by nemělo hrát roli. Problém je, že pokud něco vyberu v selectBoxu, tak se mi sice v pořádku zobrazí co chci, ale překlikne se vybraná položka na první kolonku, takže vlastně nevím co mám vybráno, chtěl bych to obejít AJAXem, aby se reloadoval jen <div> do kterého se obsah vykresluje. Koukal jsem na přednášku Vojty Dobeše, pochopil jí, ale ani tak mi to nefunguje.
Nechám mluvit kód:

@layout.latte (nevím, jestli to jde takhle použít na <div> a nebo to musí být odkaz

<script>

$("div.ajax").live("click", function (event) {
    event.preventDefault();
    $.get(this.href);
});
    </script>

HomepagePresenter

public function createComponentArticleMenu()
    {

    $control = $this->articleMenuFactory->create();
    $control['articleMenu']->onSuccess[] = function($form) {

    if ($this->isAjax()) {
        $this->invalidateControl('articles');
    }
    else {
        $this->redirect('this', array('category' => $form->values->category));
    }


    };
        return $control;

    }

default.latte (k HomepagePresenteru)

{block content}
<br /> <br />
<div class="main"><h3>Posledních 10 příspěvků:</h3> <br />
 {control articleMenu}
 {snippet articles}
 <br /> <br />
 <div class="ajax">
 <table class="table-article-create">
	<thead>
		<tr>
			<th>ID</th>
			<th>Název</th>
			<th>Text</th>
			<th>Autor</th>
			<th>Datum</th>
			<th>Kategorie</th>
		</tr>
	</thead>
	<tbody>
	    <tr n:foreach = "$articles as $article">

    <td>{$article->id}</td>
    <td>{$article->title|striptags}</td>
    <td class="content">

    <span class='icons'>
	<a n:href='Articles:edit $article->id'><img src="{$basePath}/images/icons/edit.png" /></a>

	<a n:href='Articles:delete $article->id'><img src="{$basePath}/images/icons/close-tab.png" /></a>

    </span>
	{!$article->content|substr:0,35|striptags}
    </td>
    <td>{$article->author ? $article->author:('Neznámý')}</td>
    <td>{$article->datetime}</td>
    <td>{($category = $article->ref('categories','categories_id')) ? $category->name:('Nezařazeno')}</td>

 </tr>
</tbody>
 </table>
    {/snippet}
</div>

{/block}

Díky za nápady, bude tam někde nějaká kravina, ale nemůžu na ní přijít. Script od Honzy Marka includovaný mám.

Tomáš Votruba
Moderator | 1114
+
0
-

Ahoj, pro zajaxování použij nette.ajax.js.

Aktivuj:

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

A pak k odkazu přidej class="ajax".

Mělo by to jet.

Editoval Tomáš Votruba (3. 8. 2014 23:01)

Freestyler
Člen | 50
+
0
-

Použil jsem, ale nefunguje. Nevím jak to napsat pro ten <div> co tam mám. To co chci ajaxovat, resp. má to být akce při kliknutí na button je komponenta, kterou nevykresluju ručně, ale přes {control}.

Momentálně tedy to mám takhle: http://pastebin.com/8Ctmc5Z6

Nechci tu moc spamovat, tak pastebin :).

Dále nevím jestli někde musím zjišťovat jestli je to Ajax nebo ne ($this->isAjax()), popř. někde se používá $this->invalidateControl(), který je teď deprecated, takže je v tom všem fakt zmatek :(.

Tomáš Votruba napsal(a):

Ahoj, pro zajaxování použij nette.ajax.js.

Aktivuj:

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

A pak k odkazu přidej class="ajax".

Mělo by to jet.

CZechBoY
Člen | 3608
+
0
-

Ten div.ajax dej pryč, o ničem…
Dej tam snippet articles jak máš teď. Pokud chceš jaxově vypsat komponentu tak ji dej celou do snippetu.
Něco jako

{snippet menu}
{control articleMenu}
{/snippet}

Místo invalidateControl použij redrawControl

Zax
Člen | 370
+
0
-

<div class="ajax"> jsem ještě nikdy neviděl, jak psal @TomášVotruba, class="ajax" patří do odkazu, nette.ajax.js si to pak pošéfuje. Teď si nepamatuji, jak je to u formulářů (mám to zapouzdřené do $form->enableAjax()) – buď by mělo stačit přidat class="ajax" celému formuláři, nebo na submit. Jedno z toho by mělo jít ;-)

Každopádně, element (nemusí to být nutně div), který chceš reloadovat === snippet.

invalidateControl() a validateControl() jsou deprecated a místo nich se používá metoda redrawControl(), jejíž název dává mnohem větší smysl.

Snad jsem aspoň trochu pomohl.

Freestyler
Člen | 50
+
0
-

Tak asi budu potřebovat nakopnout víc, než jsem myslel.

Tady tvořím komponentu:

protected function createComponentArticleMenu() {

     $form = new Form();

     $form->addSelect('category','Kategorie', $this->articles->getArticleCategories()->fetchPairs('id', 'name'))
	->setPrompt('Všechny články');
     $form->addSubmit('submit', 'Zobrazit');
     return $form;
}

Snippet je v tom pastebinu předtím, pro úplnost http://pastebin.com/8Ctmc5Z6. Kam teda mám nacpat class=‚ajax‘? Komponentu nevykresluji ručně, ale přes {control articleMenu}, což je vidět i v tom pastebinu, stejně tak naincludovaný init.

Nechci ajaxovat komponentu, ale výsledek z té komponenty, což je tabulka se seznamem článků z DB.

To se děje zde:

<?php

namespace AdminModule;


/**
 * Homepage presenter.
 */
class HomepagePresenter extends BasePresenter
{

     /**
     * @var \App\Components\IArticleMenuFactory
     * @inject
     */

    public $articleMenuFactory;

    /** @persistent */
    public $category;
    /**
     *
     * @return ArticleMenu
     */
    public function createComponentArticleMenu()
    {

    $control = $this->articleMenuFactory->create();
    $control['articleMenu']->onSuccess[] = function($form) {

        $this->redirect('this', array('category' => $form->values->category));



    };
        return $control;

    }

    public function renderDefault() {
         if($this->category) {
              $this->template->articles = $this->articles->getArticleByCategoryId($this->category);
         } else {
              $this->template->articles = $this->articles->fetchAll();

         }


    }

    }

Tím pádem nevím ani kam dát $this->redrawControl('articles);

Díky moc, jsem asi dost natvrdlej :(

Zax
Člen | 370
+
0
-

Zkus do createComponentArticleMenu() přidat
$form->getElementPrototype()->addClass('ajax');

Formulář musíš zajaxovatět, jinak se ti prostě ajaxově nepošle. To, že po odeslání chceš jen překreslit konkrétní snippet, dáš najevo právě tím redrawControl().

EDIT: redrawControl() patří do onSuccess (resp. obecně do signálů), což máš správně ;-)

Editoval Zax (4. 8. 2014 22:11)

Freestyler
Člen | 50
+
0
-

Form už má class ajax, to je v pohodě, ale když kouknu do firebugu, tak v síti nikde nevidím, že by tam byl nějakej JSON :(.

redrawControl jsem hodil sem

public function renderDefault() {
         if($this->category) {
	     $this->template->articles = $this->articles->getArticleByCategoryId($this->category);
	      $this->redrawControl('articles');
         } else {
              $this->template->articles = $this->articles->fetchAll();


         }

→ takže by se měl provést při vykreslování článků a danýho snippetu

Každopádně to pořád nechodí a chybu už tam nikde nevidím :/

Zax napsal(a):

Zkus do createComponentArticleMenu() přidat
$form->getElementPrototype()->addClass('ajax');

Formulář musíš zajaxovatět, jinak se ti prostě ajaxově nepošle. To, že po odeslání chceš jen překreslit konkrétní snippet, dáš najevo právě tím redrawControl().

EDIT: redrawControl() patří do onSuccess (resp. obecně do signálů), což máš správně ;-)

Zax
Člen | 370
+
0
-

Ale nééé, redrawControl nepatří do render, ale do signálu :-)

Když otevřeš stránku neajaxově, tak ti ta metoda redrawControl v renderu neudělá vůbec nic. Nevím, jestli by to ajaxově takhle fungovalo, ale mám pocit, že v renderu je na redrawControl pozdě, je třeba využít toho, že se signály provádí dřív, než render.

Po odeslání formuláře (což proběhne ajaxově, pokud má form class=„ajax“) se zavolá ta tvoje funkce $control['articleMenu']->onSuccess[] = function..., což je klasický signál (byť to tak na první pohled možná nevypadá) a proto máš jistotu, že se provede dřív, než render. V tom signálu tedy provedeš vše, co je potřeba (změníš stav – nastavíš kategorii) a označíš snippet k překreslení pomocí redrawControl().

Vidím, že tam děláš redirect se změnou persistentního parametru – redirect se ale při ajaxu neprovede (resp. provede se klasický neajaxový redirect), proto tam musí být to if($this->isAjax()). Já bych to zkusil třeba takto:

// tohle je ta tvoje onSuccess funkce

if($this->isAjax()) { // pokud je to ajax
	$this->category = $form->values->category; // nastav kategorii
	$this->redrawControl('articles'); // a označ snippet 'articles' k překreslení pomocí ajaxu
} else { // pokud není ajax, proveď klasický redirect
	$this->redirect('this', array('category' => $form->values->category));
}

Ještě jedna věc – název redrawControl() je možná trošku zavádějící, ono se neprovede okamžité překreslení, jen se označí daný snippet k překreslení a Nette k tomu přihlíží až během vykreslování („Hele, Nette, až budeš někdy v budoucnu vykreslovat tuto komponentu, tak ji nevykresluj celou, ale místo toho mi pošli tento snippet“ – a je blbost toto po Nette chtít až během vykreslování ;-)).

Příspěvek ber s rezervou, nejsem žádný Nette guru a možná jsem plácnul i pár blábolů, ale s Ajaxem jsem si hrál celkem dost.

CZechBoY
Člen | 3608
+
0
-

No já tam vidim control leda na menu.
Jestli chceš ajaxovat jen ten výpis článků tak dej ten výpis článků do snippetu.
šablona komponenty

{snippet articlesContainer}
<div n:foreach="$articles as $article" class="article">
	<h4>{$article->heading}</h4>
	<div class="date">{$article->date}</div>
	<div class="text">{$article->text}</div>
</div>
<a class="ajax" n:if="$page" n:href="next! $page">Další strana</a>
{/snippet}

Komponenta

/** @var int */
private $page = 1;

public function handleNext($page) {
   $this->page = $page;

   if ($this->presenter->isAjax()) { // musí tu být vůbec presenter?
      $this->redrawControl();
   }
}
public function render() {
   $this->template->articles = $data_z_databaze->page($this->page);
   $this->template->page = $jsou_dalsi_data_k_dispozici ? $this->page + 1 : false;

   $this->template->setFile(...);
   $this->template->render();
}
Freestyler
Člen | 50
+
0
-

Pecka, sice jsem si to trošku upravil, ale už to funguje jak má :). Díky moc moc moc!!!

Zax napsal(a):

Ale nééé, redrawControl nepatří do render, ale do signálu :-)

Když otevřeš stránku neajaxově, tak ti ta metoda redrawControl v renderu neudělá vůbec nic. Nevím, jestli by to ajaxově takhle fungovalo, ale mám pocit, že v renderu je na redrawControl pozdě, je třeba využít toho, že se signály provádí dřív, než render.

Po odeslání formuláře (což proběhne ajaxově, pokud má form class=„ajax“) se zavolá ta tvoje funkce $control['articleMenu']->onSuccess[] = function..., což je klasický signál (byť to tak na první pohled možná nevypadá) a proto máš jistotu, že se provede dřív, než render. V tom signálu tedy provedeš vše, co je potřeba (změníš stav – nastavíš kategorii) a označíš snippet k překreslení pomocí redrawControl().

Vidím, že tam děláš redirect se změnou persistentního parametru – redirect se ale při ajaxu neprovede (resp. provede se klasický neajaxový redirect), proto tam musí být to if($this->isAjax()). Já bych to zkusil třeba takto:

// tohle je ta tvoje onSuccess funkce

if($this->isAjax()) { // pokud je to ajax
	$this->category = $form->values->category; // nastav kategorii
	$this->redrawControl('articles'); // a označ snippet 'articles' k překreslení pomocí ajaxu
} else { // pokud není ajax, proveď klasický redirect
	$this->redirect('this', array('category' => $form->values->category));
}

Ještě jedna věc – název redrawControl() je možná trošku zavádějící, ono se neprovede okamžité překreslení, jen se označí daný snippet k překreslení a Nette k tomu přihlíží až během vykreslování („Hele, Nette, až budeš někdy v budoucnu vykreslovat tuto komponentu, tak ji nevykresluj celou, ale místo toho mi pošli tento snippet“ – a je blbost toto po Nette chtít až během vykreslování ;-)).

Příspěvek ber s rezervou, nejsem žádný Nette guru a možná jsem plácnul i pár blábolů, ale s Ajaxem jsem si hrál celkem dost.