Subrequest na signál, který vrací jako response Script

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

Ahoj,

jde mi o to mít možnost například při smazání řádku v nějakém seznamu přes AJAX vrátit JavaScript, který se má vykonat.

Tedy například vrátí

<script type="text/javascript">
    $('row-id').slideUp('fast', function() {$(this).remove()});
</script>

místo seznamu všech snippetů.

Taková technika se používá třeba v RoR nebo já jsem si jí implementoval do Zendu. Implementoval bych ji i do Nette, ale nějak mi pak nabourává myšlenku Nette, kdy jeden subrequest může změnit komponenty o kterých třeba ani neví. Tak jsem zkoušel vymyslet řešení, které bude umožňovat přidat do JsonResponse nějaký additionalClientScript. To už mi přijde poměrně čisté a zachovává to koncept.
Dají se tím udělat třeba efekty, že něco zmizí nebo blikne (YFT). Přes JSON payload to jde pomocí událostí také, vím, ale nezdá se mi to tak elegantní.

Konečně proč to sem píšu, zajímá mě, jestli to už třeba někdo neřešil, nebo alespoň jaký na to máte názor.

--
Jenom upřesním YFT je myšleno Yellow Fade Technique.

Editoval Trunda (20. 1. 2011 22:30)

Filip Procházka
Moderator | 4668
+
0
-

Doporučil bych najít tenhle soubor v projektu

https://github.com/…ery.nette.js

a patřičně si ho zkrášlit

Trunda
Člen | 26
+
0
-

Jo to je mi jasné, ale zdálo se mi to málo elegantní. Chtěl jsem JS, který má udělat nějaký efekt či co, nějak svázat k danému signálu presenteru.

Nakonec mi z toho vylezlo tohle http://ajaxnette.trunecek.net/.

Když klikneš na smazat, udělá se AJAX request (na signál) a ten vrátí normálně v JSON všechny snippety, state, ale navíc script pole, které obsahuje JS kód pro vykonání za

  1. ‚before‘ – před vyplněním snippetů (tady je to extrémně zajímavé, viz http://ajaxnette.trunecek.net/…query.ujs.js, čeká se tam na animace)
  2. ‚instead‘ – místo vyplnění snippetů (snippety se pomocí JS vůbec nepřekreslí)
  3. ‚after‘ – až po překreslení snippetů

Samozřejmě jsem musel upravit jquery.nette.js, navíc jsem tam přidal ještě nějaké featury (inspirace u RoR UJS)

Sepsal jsem si na to vlastní abstract presenter s nativní podporou takových „AJAX skriptů“, celý zdroják ukázky je tady http://ajaxnette.trunecek.net/src.zip

Možná je to prasárna, a proto to sem dávám, co si o tom myslíte?

Filip Procházka
Moderator | 4668
+
0
-

To je hrozné :D

osobně bych si definovat nějaké „styly“ nebo něco jednotného, co se má provést, při určitém typu akce a předával pak typ akce

$this->payload->actionType = "delete";

a podle toho pak vykonal určitou akci.

Mimochodem všiml sis co ti to generuje za odkazy, když něco smažeš?

//edit: jak tak na to koukám tak je to možná i schválně, protože se tam přidávají pořád za sebe „(snippet načten)“ ale stejně jsem se lekl :P

Editoval HosipLan (21. 1. 2011 8:01)

Trunda
Člen | 26
+
0
-

Ano, to mě také napadlo, ale řekl bych, že tohle tolik nesvazuje variabilitu použití. Co se ti zdá hrozné :-), forma implementace nebo forma prezentace?


Ano to co se generuje za odkazy je správně, aby bylo vidět, že snippet se překreslí až po vykonání ‚before‘ skriptu.

Editoval Trunda (21. 1. 2011 10:54)

Filip Procházka
Moderator | 4668
+
0
-

Je to hrozné, protože pleteš do presenterů javascript.

Trunda
Člen | 26
+
0
-

Koukal jsi na ten kód? V prezenteru není ani kousek javascriptu.

class HomepagePresenter extends AjaxScriptsSupportPresenter
{

    /** @persistent */
    public $list = array(
        'Položka 1',
        'Položka 2',
        'Položka 3',
        'Položka 4',
        'Položka 5',
        'Položka 6',
        'Položka 7',
    );

    public function handleDelete($index)
    {
        if (isset($this->list[$index])) {
            unset($this->list[$index]);
            foreach ($this->list as $i => $v) {
                $this->list[$i] = $v . ' (snippet načten) ';
            }
        }
        if (!$this->isAjax()) {
            $this->redirect('this');
        } else {
            $this->ajaxTemplate->index = $index;
        }

        $this->invalidateControl();
    }

    public function renderDefault()
    {
        $this->template->list = $this->list;
        $this->template->listCount = count($this->list);
    }

}
abstract class AjaxScriptsSupportPresenter extends \Nette\Application\Presenter
{
    /**
     * Template for ajax scripts
     * @var \Nette\Templates\IFileTemplate
     */
    protected $ajaxTemplate = null;
    protected $ajaxSignalName = null;



    protected function startup()
    {
        parent::startup();
        if ($this->isAjax() && $this->signal !== null
                && $this->signal[0] === '') {
            $this->ajaxTemplate = $this->createTemplate();
            $this->ajaxSignalName = $this->signal[1];
        }
    }

    protected function beforeRender()
    {
        parent::beforeRender();
        if ($this->ajaxTemplate !== null && $this->ajaxSignalName !== null) {
            $this->ajaxTemplate->control = null;
            $this->ajaxTemplate->presenter = null;
            $templateFiles = $this->formatAjaxTemplateFiles($this->getName(),
                    $this->ajaxSignalName);
            foreach(array('before', 'instead', 'after') as $pos) {
                foreach($templateFiles as $file) {
                    if (\is_readable(sprintf($file, $pos))) {
                        if (!isset($this->payload->script)) {
                            $this->payload->script = array();
                        }
                        $this->ajaxTemplate->setFile(sprintf($file, $pos));
                        $this->payload->script[$pos] = (string) $this->ajaxTemplate;
                        break;
                    }
                }
            }
        }
    }

    public function formatAjaxTemplateFiles($presenter, $signal)
    {
        $appDir = Environment::getVariable('appDir');
	$path = '/' . str_replace(':', 'Module/', $presenter);
	$pathP = substr_replace($path, '/templates', strrpos($path, '/'), 0) . '/ajax';
	return array(
		"$appDir$pathP/$signal.%s.latte",
		"$appDir$pathP/$signal.%s.phtml",
	);
    }

}

Každý signál daného prezenteru pak může mít js, pokud např. v templates/Homepage/ajax/ uděláš šablonu delete.before.latte a tam napíšeš ten JavaScript. Takže v tomto připadě mám

{* templates/Homepage/ajax/delete.before.latte *}
$("#item-{$index}").fadeOut(2000);

a

{* templates/Homepage/ajax/delete.after.latte *}
alert('Smazal jsem to.');

Editoval Trunda (21. 1. 2011 14:03)

Filip Procházka
Moderator | 4668
+
0
-

no to je svým způsobem sexy, ale stejně si myslím, že by se to dalo omezit na typy akcí :)