Něco jako nette.js s jQuery

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

Aktuální informace o ajaxu a ajaxových formulářích

Editoval Honza M. (15. 6. 2009 17:04)

David Grudl
Nette Core | 8166
+
0
-

Super! Díky!

ad obarvování: je to lepší?

Honza Marek
Člen | 1664
+
0
-

formuláře

Editoval Honza M. (15. 6. 2009 17:05)

ViliamKopecky
Nette hipster | 230
+
0
-

Honza M. napsal(a):

Tak za tohle děkuju ještě víc :)

Editoval enoice (9. 2. 2009 15:16)

David Grudl
Nette Core | 8166
+
0
-

Můžu to použít (samozřejmě s uvedením tvého autorství) v examples?

Honza Marek
Člen | 1664
+
0
-

Jasně, od toho to tu je, aby se to používalo.

Honza Marek
Člen | 1664
+
0
-

Hm… U ajaxového formuláře jsem zapomněl, že tam může být taky nějaká validace. Přidal jsem teda jednu podmínku navíc, která spustí validaci, co je v parametru onsubmit.

zdvori
Člen | 10
+
0
-

Zatim nefunguje pro MultipleSelectBox, kde se jako hodnota posila pole. V nasledujicim kodu v „values“ jeste je:

CONTACTS[] = 1
CONTACTS[] = 2

ale v
sendvalues uz je logicky jen posledni:

CONTACTS[] = 2
<script>
var values = form.serializeArray();

                for (var i = 0; i < values.length; i++) {
                        sendValues[values[i].name] = values[i].value;
                }

</script>
Honza Marek
Člen | 1664
+
0
-

Chvíli jsem nad tím děsně koumal, ale nakonec jsem přidal podporu multi selectu.

Jod
Člen | 701
+
0
-

Honzo, feature request ,)

Keď chcem spraviť pekný prechod medzi updatom snippetu, napríklad fade, použijem:

<script>
                        if (data.snippets) {
                                for (var i in data.snippets) {
                                        $("#" + i).fadeOut('fast', function() {
                                          $("#" + i).html(data.snippets[i]);
                                          $("#" + i).fadeIn('fast');
                                        });
                                }
                        }
</script>

Nedá sa to spraviť nejak tak, aby som si mohol takýto efekt nastaviť bez toho aby som sa babral do tvojho pluginu? Dík.

Editoval Jod (21. 2. 2009 12:50)

Etch
Člen | 403
+
0
-

Honzo taky ti sem přidám něco do mlýnku. Možná, že by bylo dobré použít jiný selector při updatu snippetů, protože tak jak to máš ty se připravuješ o možnost využití stejného „snippetu“ dvakrát:

<?php
@{? $grid->renderPaginator()}
@{? $grid->render()}
@{? $grid->renderPaginator()}
?>

Toto by sice fungovalo, ale překreslovalo by se pouze horní stránkování. Proto by se tam pro překreslování snippetů hodil více například následující kód :

<script>
	if (data.snippets) {
		for (var i in data.snippets) {
			$("[id='"+ i +"']").html(data.snippets[i]);
		}
	}
</script>

Ten již zajistí, že se vždy přepíší všechny potřebné snippety.

Editoval Etch (23. 2. 2009 19:14)

Honza Marek
Člen | 1664
+
0
-

Id by mělo teoreticky být unikátní. Pokud není, stránka je nevalidní a jsou s tím tyhle problémy.

Ideální by bylo, kdyby snippety byly označeny třídu a ne přes id. Anebo hlídat unikátnost toho id. To by mělo drobnou nevýhodu v tom, že by se takový vícenásobný snippet při aktualizaci přenášel vícekrát. K tomu se ale nějak musí postavit David, změna selektoru je spíše takový hack.

Etch
Člen | 403
+
0
-

Ano id by mělo být unikátní, ale dokud se tohle nevyřeší není moc na výběr. Označování snippetů pomocí ID jim podle mého názoru bere jednu z jejich silných zbraní. Osobně bych asi taky radši označoval snippety pomocí class.

Ondrej
Člen | 110
+
0
-

Etch napsal(a):

<?php
@{? $grid->renderPaginator()}
@{? $grid->render()}
@{? $grid->renderPaginator()}
?>

Zrovna toto jsem resil, snazil jsem se i generovat nazev snippetu dynamicky v sablone napr. renderPaginator(‚bottom‘), ale nic nefungovalo spravne, takze jsem to nakonec vyresil takto:

<?php
 {snippet paginator}
 @{? $grid->renderPaginator()}
 {/snippet}
 @{? $grid->render()}
 {snippet bottom_paginator}
 @{? $grid->renderPaginator()}
 {/snippet}
?>
Honza Marek
Člen | 1664
+
0
-

Keď chcem spraviť pekný prechod medzi updatom snippetu, napríklad fade:

Přerozdělil jsem původní plugin do několika funkcí, jedna z nich je jQuery.updateSnippet. To v praxi znamená, že se dá snadno nahradit. Třeba takto:

jQuery.extend({
	updateSnippet: function (id, data) {
		$("#" + id).fadeOut("fast", function () {
			$(this).html(data).fadeIn("fast");
		});
	}
});

No a už se to hezky mění s efektem fade.

Jod
Člen | 701
+
0
-

Díky moc, si borec :)

Jod
Člen | 701
+
0
-

Po callbacku je treba znova zaregistrovať niektoré veci, ktoré nejdú cez jQuery.live ako napr texyla, alebo

	// external links
	$('a.external').attr('target', '_blank');
	// hidden files
	$('.hidden').attr('style', 'display: none');

Ak to ide nejak inak tak fajn, ja som si to spravil zatiaľ tak, že pridám do ajaxCallback za updaty snippetov funkciu jQuery.registerAfterUpdate() ktorá zostane prázdna

registerAfterUpdate: function () {

},

A vo svojom scripte volám niečo takéto:

jQuery.extend({
	// updateSnippet

        registerAfterUpdate: function() {
        	// texyla
        	$.texyla();
        	// external links
        	$('a.external').attr('target', '_blank');
        	// hidden elements
        	$('.hidden').attr('style', 'display: none');
        }
});

Editoval Jod (1. 3. 2009 13:00)

Ola
Člen | 385
+
0
-

BTW:

$('.hidden').hide();

;)

czend@
Člen | 2
+
0
-

zdravim ve spolek, jsem novacek a v tomhle se zatim poradne topim, nemate proto prosim nekdo nejaky uz funkcni kousek kodu, ktery by pokryval tuhle problematiku? neco ve stylu datagridu apod. predem moc diky, PS: sorry za OT

romansklenar
Člen | 655
+
0
-

Zrovna asi před týdnem jsem si s tím hrál a zkošel na to napasovat DataGrid, tak tady máš:

czend@
Člen | 2
+
0
-

wau, tak tomu rikam rychlost reakce :) diky moc, prohrabnu to a snad se neco ujme :)

phx
Člen | 651
+
0
-

Pekny, ale bez JS se neudrzi vybrany select:) Cimz to je dost neuzivatelsky.

Jod
Člen | 701
+
0
-

Jj, ale stačilo by pridať perzistentný parameter len :)

romansklenar
Člen | 655
+
0
-

Po odeslání formuláře se selectem stačí přesměrovat (pokud nejde o ajaxový požadavek) a parametry se dostanou z POSTu do GETu a je po problému. Fixnul jsem to v tom archívu i v demu.

phx
Člen | 651
+
0
-

:) Supr…

Dokonale:))

Honza Marek
Člen | 1664
+
0
-

Není to dokonalé… Roman tam ještě nedal točítko :-D

romansklenar
Člen | 655
+
0
-

:D nejdriv musim prijit na to jak prenest to puvodni univerzalni do jQuery

jasir
Člen | 746
+
0
-

Ahoj, vypadá to, že ajax v nefunguje v IE – zřejmě potíže s jquery.live.
Ve své aplikaci jsem live() zkusil nahradit pluginem livequery, zdá se, že to šlape.

Honza Marek
Člen | 1664
+
0
-

Hm… docela nečekaná zrada. Myslel jsem, že když je to od jQuery, tak to bude fungovat všude.

jasir
Člen | 746
+
0
-

No, ono je u toho live() v dokumentaci uvedeno:
„Currently not supported: blur, focus, mouseenter, mouseleave, change, submit“, čili v tom
je zřejmě zakopaný pes. Snad to brzy dopracují. To livequery funguje prakticky stejně (akorát to funguje :)

Honza Marek
Člen | 1664
+
0
-

Tak aspoň, že funguje click.

A.
Člen | 87
+
0
-

Hezka prace Honzo M.!

LeonardoCA
Člen | 296
+
0
-

romansklenar napsal(a):

Zrovna asi před týdnem jsem si s tím hrál a zkošel na to napasovat DataGrid, tak tady máš:

v datagrid.js po funkci registerAfterUpdate je navíc čárka, která v IE shodí funkčnost javascriptů

jQuery.extend({
	//grafický efekt u překreslení všech snippetů
	updateSnippet: function (id, html) {
		$("#" + id).animate({ opacity: 0.5 }, "fast", "swing", function () {
			$(this).html(html).animate({ opacity: 1 }, "fast", "swing");

			jQuery.registerAfterUpdate();
		});
	},

	registerAfterUpdate: function() {
		// schovat odesílací tlačítka formulářů (nutno po každém překreslení snippetu)
		$("form.ajaxform :submit").hide();
	},
});

jinak v další verzi jquery 1.3.3 by už snad mělo fungovat vše i bez použití livequery

Honza Marek
Člen | 1664
+
0
-

Vymyslel jsem ještě lepší ajaxování. Místo $.netteAjax bude možné použít normální funkce $.get, $.post a podobně. Jen pomocí funkce $.ajaxSetup nastaví nějaký defaultní callback, který se postará o snippety a tak.

jQuery.extend({
	updateSnippet: function (id, html) {
		$("#" + id).html(html);
	},

	netteCallback: function (data) {
		// redirect
		if (data.redirect) {
			window.location.href = data.redirect;
		}

		// snipeti
		if (data.snippets) {
			for (var i in data.snippets) {
				jQuery.updateSnippet(i, data.snippets[i]);
			}
		}
	}
});

jQuery.ajaxSetup({
	success: function (data) {
		jQuery.netteCallback(data);
	},
	dataType: "json"
});

Použití je stejné, jen funkci $.netteAjax zaměníte za libovolnou standardní funkci, třeba $.get. Rozdíl nastane v případě, že budete chtít při dokončení ajaxového požadavku zavolat vlastní callback. Pokud budete chtít provést snippetování a podobně, tak je potřeba explicitně zavolat funkci $.netteCallback:

// chci kromě normálního zpracování ještě zobrazit hlášku
$.get("someUrl", function (data) {
	jQuery.netteCallback(data);
	alert("hláška");
});

Na závěr přidám upravený kód pro ajaxové formuláře:

jQuery.fn.extend({
	netteAjaxSubmit: function (callback) {
		var form;
		var sendValues = {};

		// odesláno na tlačítku
		if (this.is(":submit")) {
			form = this.parents("form");
			sendValues[this.attr("name")] = this.val() || "";

		// odesláno na formuláři
		} else if (this.is("form")) {
			form = this;

		// neplatný element, nic nedělat
		} else {
			return null;
		}

		// validace
		if (form.get(0).onsubmit && !form.get(0).onsubmit()) return;

		var values = form.serializeArray();

		for (var i = 0; i < values.length; i++) {
			var name = values[i].name;

			// multi
			if (name in sendValues) {
				var val = sendValues[name];

				if (!(val instanceof Array)) {
					val = [val];
				}

				val.push(values[i].value);
				sendValues[name] = val;
			} else {
				sendValues[name] = values[i].value;
			}
		}

		// odeslat ajaxový požadavek
		return jQuery.ajax({
			url: form.attr("action"),
			data: sendValues,
			type: form.attr("method") || "get",
			success: callback
		});
	}
});

Editoval Honza M. (8. 4. 2009 23:48)

Ola
Člen | 385
+
0
-

Neměli by jste někdo kód (v jQuery), který by točítko přesunul na požadovaný místo? (teď to řešim tak, že schovám vše a zobrazim ho uprostřed) Za předpokladu že využuju ten kód od Honzy M.

Honza Marek
Člen | 1664
+
0
-

Kód nemám, protože mi točítko uprostřed vyhovuje, ale udělalo by se to při kliknutí na odkaz asi takto (jestli jsem dobře pochopil z dokumentace ten jQuery offset).

$("a.ajax").click(function () {
	$.get(this.href);
	var ofs = $(this).offset();
	$("#tocitko").css({
		left: (ofs.left + 10) + "px",
		top: (ofs.top + 20) + "px"
	});
	return false;
});
Honza Marek
Člen | 1664
+
0
-

Vymazlil jsem točící kolečko u odkazu a dal příklad do nette extras.

Ola
Člen | 385
+
0
-

Díky, to je to co sem potřeboval :)

Editoval Ola (21. 4. 2009 6:28)

David Grudl
Nette Core | 8166
+
0
-
romansklenar
Člen | 655
+
0
-

Píšeš tam jako ukázku:

	// nastaví událost onclick pro všechny elementy A s třídou 'ajax'
	$("a.ajax").live("click", function(event) {
		$.get(this.href); // zahájí AJAXový požadavek

		// zobrazí spinner, signalizující uživateli, že se něco děje
		$('<div id="ajax-spinner"></div>').css({
			position: "absolute",
			left: event.pageX + 20,
			top: event.pageY + 40

		}).ajaxStop(function() {
			$(this).remove(); // po skončení spinner smaž

		}).appendTo("body");

		return false;
	});

To zobrazení toho ajaxloaderu nemusí být nutně v těle té funkce, může být i mimo a pak se bude provádět při každé detekci ajaxového požadavku, to je myslím jedna z jeho velkých předností :) možná by bylo zajívé pro čtenáře to tam zmínit (pokud to ještě jde upravit) než si to ráno budou lidi číst.

David Grudl
Nette Core | 8166
+
0
-

romansklenar napsal(a):

To zobrazení toho ajaxloaderu nemusí být nutně v těle té funkce, může být i mimo a pak se bude provádět při každé detekci ajaxového požadavku, to je myslím jedna z jeho velkých předností :) možná by bylo zajívé pro čtenáře to tam zmínit (pokud to ještě jde upravit) než si to ráno budou lidi číst.

Teď asi nerozumím. Ajaxloaderem myslíš ten obrázek? A proč se nezobrazuje po ajaxStart() ?

romansklenar
Člen | 655
+
0
-

Vlastně jo, chybí tam ajaxStart, proto to nejede …už tam vidím něco co tam není.

	$("a.ajax").live("click", function () {
		$.get(this.href);
		return false;
	});

	// zobrazí spinner, signalizující uživateli, že se něco děje
	$('<div id="ajax-spinner"></div>').css({
		position: "absolute",
		left: event.pageX + 20,
		top: event.pageY + 40

	}).ajaxStart(function () {
		$(this).show();
	}).ajaxStop(function() {
		$(this).hide();
	}).appendTo("body").hide();
David Grudl
Nette Core | 8166
+
0
-

Už asi rozumím. A ty vyvoláváš Ajaxové požadavky i jiným způsobem, než přes tu knihovnu?

Honza Marek
Člen | 1664
+
0
-

David Grudl napsal(a):

Honzo, jsi na Zdrojáku

Hezký… :-)

Už asi rozumím. A ty vyvoláváš Ajaxové požadavky i jiným způsobem, než přes tu knihovnu?

Když se spinner zobrazuje pomocí ajaxStartu, tak se zobrazí při jakémkoliv požadavku daném přes jQuery. Což kromě kliknutí na odkaz může být třeba ajaxové odeslání formuláře.

Tomik
Nette Evangelist | 485
+
0
-

Ahoj Honzo!

Jednak musím říct: skvělá práce! :)

A teď dotaz :) –

Mám takovou situaci – vykresluju tabulku, podobnou DataGridu, která obsahuje výpis adresářové struktury, vše jede jak má, ale kvůli multifile uploadu používám knihovnu http://www.uploadify.com .. ta podporuje spuštění kódu po provedení uploadu, a to pomocí fce onComplete (viz http://www.uploadify.com/documentation/), do této fce, která se pouští po provedení uploadu jsem umístil $.get({link list! $path, $orderBy, $rename}) → aby došlo k obnovení dat v snippetu. Požadavek se provede (FireBug mi jej zobrazí a je vidět, že Nette vrátí validní JSON odpověď s aktuální tabulkou (vč. nově uploadnutých souborů), jenže Tvůj JS kód jej nepřekreslí…

Původně jsem si myslel, že to je asi v kolizi s tou uploadovací knihovnou, tak jsem ji dal pryč a zkusil pouze něco ve smyslu:

function akce() {
    $.get({link list! $path, $orderBy, $rename});
}

setTimeout("akce()", 5000);

A opět, JSON obsahoval správný výsledek, ale ten nepřepsal, to co bylo starší.

Ptám se tedy: nevíš, kde by mohla být chyba?

Díky!

Honza Marek
Člen | 1664
+
0
-

No nevím… Zavolá se vůbec ten $.netteCallback?

Tomik
Nette Evangelist | 485
+
0
-

Honza M. napsal(a):

No nevím… Zavolá se vůbec ten $.netteCallback?

Ha, neprovede, ale jinak se provádí (ať už pokud jde o ajaxlink nebo o ajaxform)…

Honza Marek
Člen | 1664
+
0
-

Tak ho zavolej… asi ho něco přepisuje.

Tomik
Nette Evangelist | 485
+
0
-

Aha, tak už jsem na to přišel. Dík!

Nakonec místo $.get({link list! $path, $orderBy, $rename}); volám $.netteAjax({link list! $path, $orderBy, $rename}); – původně jsem myslel, že i ten $.get zavolá callback sám, ale evidentně ne. To je jedno, problém vyřešen! :)

Editoval Tomik (27. 4. 2009 22:18)

Honza Marek
Člen | 1664
+
0
-

No když nahraješ novou verzi toho pluginu, která je na webu v extras, tak tam žádná funkce $.netteAjax není a $.get opravdu volá $.netteCallback sám.