nette.ajax.js – alt. obsluha pro AJAX s jQuery

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Všechna čest oficiálnímu AJAX skriptu, co je v doplňcích. Slouží výborně. Nicméně jsem se přistihnul, že ve všech projektech ho upravuju. Což nemusí být nutně špatně, on přeci jen velkým vývojem neprochází, takže nějaké updaty mě zatím netrápily. Nicméně už pro dobrý pocit bych jej raději konfiguroval zvenku než přepisoval na místě.

Napsal jsem si tento alternativní skript. Je o něco větší, je konfigurovatelný a zahrnuje některé z doporučení z přednášky Jakuba Vrány na Webexpu.

Github: https://github.com/…ette.ajax.js

pepakriz
Člen | 246
+
0
-

Tohle bylo už delší dobu potřeba, díky!

Filip Procházka
Moderator | 4668
+
0
-

Super práce!

Jan Tvrdík
Nette guru | 2595
+
0
-

Mít hotový nový addon portál, dal bych ti like!

Návrh na zlepšení: Zkus ten kód okomentovat pomocí jsDoc.

uestla
Backer | 796
+
0
-

Přidávám se s velkým díky!

Už u HonzoMarkova skriptu mi chyběla podpora obrázkových tlačítek (včetně odeslání offsetových souřadnic). Nicméně jsem si forknul, a až budu mít více času na zprovoznění, zkusím něco sesmolit a poslat pull.

pepakriz
Člen | 246
+
0
-

Mám problém s řádkem 86 if (/:|^#/.test(url)) return;. Vrací true a ajax se neprovede (po zakomentování vše funguje). V odkazech používám relativní cesty, přesto je v proměnné this.href cesta absolutní.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Pokud se nemýlím, tak to kontrola vyřadí odkazy na hash, tedy v rámci stránky. Nemáš v těch odkazech hash?

Tohle by chtělo vyladit, aby povolil odkazy obsahující hash, které ale vedou jinam. Pokud jsem testoval dobře, tak teď to odkaz s hashem vedoucí na jinou URL AJAXově neprovede.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Jinak snad se mi podaří to celé rozložit ještě víc :). Rád bych i tuhle kontrolu vyčlenil jako extension.

pepakriz
Člen | 246
+
0
-

hash tam nemám, jen GET proměnné. Ale pokud ten regulár chápu správně, tak to projde i na stringy, které obsahují dvojtečku.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Hm, pravda pravdoucí, on ten regulár hlídá i absolutní odkazy :). A this.href (tedy na AnchorElementu) evidentně vždy obsahuje absolutní cestu. Tento fix by to měl řešit.

pepakriz
Člen | 246
+
0
-

Díky, vyřešeno

pepakriz
Člen | 246
+
0
-

Plánuje se i podpora historie u formulářů? Dokuď používám formuláře, které přesměrovávají na this, tak historie není potřeba. Jak ale vyřešit situaci, kdy po uložení formuláře má dojít k přesměrování na jinou akci? Upravil jsem JS pro podporu formulářů od řádku 236 nějak takto:

		if ($el.is('a')) {
			this.href = ui.href;
		}else if($el.is('input')){
			this.href = $el.parents('form:first').attr('action');
		} else if($el.is('form')) {
			this.href = $el.attr('action');
		}

Má to ovšem dvě nevýhody.

  1. action formuláře musí vést na cílové URL ⇒ Továrnička na formulář se musí nacházet i na výsledné URL.
  2. V historii zůstává v URL GET parametr do.
uestla
Backer | 796
+
0
-

Jak jsem se původně chvástal, tak jsem nakonec snad úspěšně implementoval obrázková tlačítka (pull zde). Bohužel jsou v tom pull requestu zahrnuté další nesouvisející commity, s git(hub)em neumím :(

Editoval uestla (10. 6. 2012 15:23)

jtousek
Člen | 951
+
0
-

Ty další commity dávají smysl, takže to snad vadit nebude.

uestla
Backer | 796
+
0
-

Fakt se mi tahle novinka líbí, protože je navržená dost robustně a pokrývá v podstatě všechno, co mi u Honzomarkova skriptu chybělo, super!

Proto bych rád fixnul všechno, co haprovalo už dříve, či robí problémy stále…

Na jednu takovou věc jsem právě narazil, a sice dvojitá javascriptová validace – nevím, zda na to už někdo narazil. Nevím, kde přesně to nastává, každopádně navěšuje se v netteForms.js, a následně se volá dvakrát:

  • jednou „interně“ v prohlížeči při submitu formuláře
  • a podruhé ručně v tomhle místě – což považuji za přežitek ze starého skriptu HM

Nicméně validace před odeslání AJAXového požadavku je samozřejmě nutná, bohužel ani když místo odkazované konstrukce použiji if (!Nette.validateForm($el[0])) return null;, alerty vyskočí opět dva.

Nebo je chyba někde úplně jinde? Či někoho napadá jiné, především pak funkční řešení?

cubic
Člen | 45
+
0
-

Super, chybi mi tam ale jeste jedna vec. Je fajn, ze to pracuje s history.pushState, ale uz to nedokaze nacist predchozi stav pri kliknuti na zpet v prohlizeci, coz je podle me celkem zasadni. S puvodnim skriptem tuhle upravu vyuzivam hlavne pro „zajaxovateni“ aplikace a presenteru. U HTML5 pres popstate, pro Internet Explorer pres hashbang pomoci pluginu jQuery BBQ – http://benalman.com/…-bbq-plugin/ – v tomhle pripade to plati nejen pro historii, ale i pri zadani adresy do prohlizece, kdy je potreba nacist aktualni stav, napr. https://nette.org/#…. Kod muzu v pripade zajmu poskytnout.

Filip Procházka
Moderator | 4668
+
0
-

Uplne nejlepsi by bylo to upravit a poslat pull request :)

cubic
Člen | 45
+
0
-

Souhlas :-) Ale JS neni moje parketa, tak bych to nejen pro zachovani cistoty kodu prenechal na nekom jinym. Navic asi neni uplne idealni pouzivat dalsi cizi pluginy (BBQ). Pokud by se do toho nekdo chtel pustit, rad poslu moje reseni vcetne vysvetleni, jak jsem rekl.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Já jsem ohledně Pull requestů velmi otevřený :) Nebo otevři issue. Každopádně budu rád za nápady a ukázky řešení, protože úplně nejvíc budu rád, když se v nějaké formě podaří do nette.ajax.js začlenit.

pepakriz
Člen | 246
+
0
-

Chtěl bych mít možnost volat ajax z js tak, aby ho tento script obsloužil. Přemístil jsem si proto volání ajaxu do samostatné metody (viz commit: https://github.com/…2a1129047072). Zajímalo by mě, jestli je toto řešení správné. Ptám se i proto, že jsem si udělal základní podporu historie, která tuto změnu využívá. (commit: https://github.com/…bb9305da2213)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Bedlivě to sleduju :) Vyčlenit logiku volání $.ajax my smysl dává, buď vytvoř pull request nebo to můžu rovnou udělat vlastním commitem.

pepakriz
Člen | 246
+
0
-

Vyčlenění nechám raději na tobě. Na zbytek pak pošlu pull.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Ok. Dneska se k tomu dostanu.

pepakriz
Člen | 246
+
0
-

Super. Poslal jsem pull na popState.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Přidána podpora pro odesílání souřadnic kliknutí v obrázkovém tlačítku (thx to @uestla).

darkwind
Člen | 32
+
0
-

moc pěkné, díky :-)

na1k
Člen | 288
+
0
-

Ještě jednou díky za skript :-)
Při použití jsem narazil na nějaké drobnosti, které jsem si dovolil opravit .

Mám ale dotaz. Nejsem žádný JS guru, takže mě nenapadá čisté řešení.
Potřeboval bych přidat vlastní rozšíření, jehož obsluha se ale spustí až po proběhnutí obsluhy jiného rozšíření, v mém případě snippet. (Mým cílem je se snippetem po příchodu dále pracovat, pokud v payloadu přišly nějaké flagy, které mé rozšíření rozpozná.)

Všiml jsem si že v kódu je podpora pro vzájemné závislosti rozšíření, ale nejsem si jistý, jestli to ovlivní i pořadí jejich volání; spíše asi ne. Přijde mi to jako docela užitečná vlastnost. Podobně na History bych v budoucnu rád navěsil vlastní dodatečnou obsluhu pro navigaci vpřed a zpět.

Využil by to ještě někdo? Má smysl se nad sekvenčním voláním nějak pozastavovat anebo to mám řešit jinak? (V případě snippetů např. pomocí DOMSubtreeModified; u historie a jiných exts nevím)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Díky za opravy, můžeš je otevřít jako pull requesty (ideální je, pokud jsou jednotlivé související opravy v jednotlivých větvích, z masteru raději ne). Případně si to můžu stáhnout ručně.

Ad spouštění rozšíření: teď to nijak moc chytrý není, spouští se postupně jak byly registrované. Tzn. tvoje rozšíření se určitě spustí po snippets.

na1k
Člen | 288
+
0
-

Uf, dneska jsem na GitHubu upravoval ještě něco jiného a samo mi to vytvořilo branch patch-1, zatímco tady zase mastera. (Je někde sepsané doporučení jak při commitech řešit branche, requesty apod.?) Takže si to asi sám pickni, co uznáš za vhodné. Příště to zkusím udělat lépe.

To že se rozšíření spouštějí tak jak byly registrované jsem si všiml, ale nevím jestli je rozumné spoléhat na to, že se to chování v budoucnu nezmění. No ještě to zvážím a určitě budu sledovat další vývoj ;-)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

S verzováním nemám velké zkušenosti, ale aby v tom byl nějaký pořádek, vydávám verzi 1.0.0. Setinky pro bugfixy, desetinky pro případné BC breaky, nejvyšší verze asi pro kompletní změnu architektury. Pro Nette 3.0.

Změny:

  • Validace události (kontrola kláves při kliku na odkaz, hashový nebo absolutní odkaz atd.) vyčleněna do extension, takže ji lze jednoduše vypnout. Vypnout ji lze také uvedením klíče validate do volání $.nette.ajax(), např.:
$('#link').click(function (e) {
	$.nette.ajax({
		validate: false
	}, this, e);
});

Dokonce je možné vypínat jednotlivé části validace (místo bool parametru lze uvést hash), podporované části jsou keys, url a form (pro podrobnosti viz zdrojový kód).

  • vyčleněno bylo také zpracování hodnot formuláře
  • změnilo se rozhraní události before, nyní přijímá parametry v tomto pořadí: settingsui, `e
bojovyletoun
Člen | 667
+
0
-

Dostal jsem takový nápad ohledně historie a odkazů. Ne vždy se hodí přidávat do historie každý kliknutí ( započtení hlasu, změna řazení výpisu). Mohlo by se to nejak odlišit třídou např „nohistory“. Díval jsem se teď do aktuální verze, tam je kontrola signálu dle payloudu (to k tomu asi ani není zamýšleno). Myslítě že to najde využití?

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Popravdě přesně kvůli tomu tam to payload.signal je. Ve své aplikaci si do payloadu automaticky přidávám:

$this->payload->signal = $this->getSignal();

Nicméně to nepovažuju za hotové, spíš mi to „jen fungovalo“ :).

uestla
Backer | 796
+
0
-

Zkoušel jsem novou verzi a bohužel se validuje dvojitě pořád… Nikomu to nedělá? :-(

Pro jistotu přikládám kód, kterým inicializuji:

$.nette.ext('history', null);

$('form').livequery(function () {
	Nette.initForm($(this)[0]);
});

$.nette.init(function (handler) {
	$('a.ajax').live('click', handler);
	$('form.ajax').live('submit', handler);
	$('form.ajax :submit, form :submit.ajax, form.ajax :image, form :image.ajax').live('click', handler);
});

Měl jsem podezření, jestli to není live() eventy, ale ani po přepsání na eventy klasické se validuje dvakrát :(

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Nebude to tím, že netteForms.js (nette.forms.js vypadá fakt lépe, díky @HosipLan za inspiraci :) ) už samo na začátku automaticky initForm pro každý formulář volá?

Filip Procházka
Moderator | 4668
+
0
-

Já ti už ani nevím, na co je to .livequery dobré :)

$.nette.ext('formsValidationBind', {
	success: function (payload) {
		if (!payload.snippets) {
			return;
		}

		for (var i in payload.snippets) {
			$('#' + i + ' form').each(function () {
				Nette.initForm(this);
			});
		}
	}
})
uestla
Backer | 796
+
0
-

Inicializaci validace v netteForms.js mám právěže zakomentovanou…

22
Člen | 1478
+
0
-

@uestla: dneska jsem hledal takovou blbou chybu asi hodinu a zjistil jsem, že jsem 2× includoval head sekci a tudíž některé JS jely 2× nebo vůbec :-)

Zkus to hodit na čistý sandbox bez toho livequery, jestli problém přetrvá, a pokud to bude ok, pak následuje hra najdi 5 rozdílů :-)

Editoval 22 (6. 6. 2012 20:22)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Já ti už ani nevím, na co je to .livequery dobré :)

To je slovo :)

uestla
Backer | 796
+
0
-

Já už jsem právěže zoufalej, kontroloval jsem ± všechno a ne a ne najít příčinu.

A možná, že už jsem na to přišel :(

Jelikož navěšuju jak na tlačítko, tak na submit formuláře, nejspíš se provedou oba…

Pokavajď ale nahradím tenhle řádek následujícím,

if (!Nette.validateForm(analyze.form)) return false;

validace proběhne jen jednou. To jsem z toho tzv. blázen… Není možné, že by se někde v běhu validace/AJAX handleru nestopovala „propagace“ eventu?

uestla
Backer | 796
+
0
-

Je to tak, jak jsem si myslel…

Když přesunu stopnutí propagace a prevenci výchozího chování eventu hned na začátek, tj. na tento řádek, rázem vše funguje. Bohužel ale nemám nastudované eventy ani javascript jako takový, čili nevím, jaké nepříjemnosti by to mohlo způsobit.

Nicméně asi budu (po této úpravě) používat raději výchozí navěšování s ajax třídou, která se zdá býti funkční…

Jen otázka bokem: vypadá to, že zAJAXovatění probíhá dynamicky i bez použítí livequery, je tam nějaký trik? :-)


EDIT: Tak jsem se unáhlil… Po odkazované úpravě (přidání image button selectoru do výchozího button selectoru) dochází ke dvojité validaci rovněž. Vypadá to tedy, že je na vinně již zmíněné „pozdní“ zastavení propagace eventu.

Bude v něčem vadit přesunout to na zmínený začátek?

Editoval uestla (7. 6. 2012 14:50)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Díky za poctivé brodění se tímhle bahnem, taky se na tu validaci mrknu.

Dynamické zajaxovatění používá takový trik, že v případě success se znovu volá $.nette.load();. Proto jsou v něm ty off, aby se to nenavěšovalo vícekrát. Je to efektivnější než livequery (to je to samé co deprecated live?), protože prohlížeč nemusí tak moc naslouchat a furt všechno kontrolovat, aby mu náhodou u nějakého nového prvku událost neušla… http://paulirish.com/…jquery-live/

Editoval vojtech.dobes (7. 6. 2012 23:29)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Bylo to buglý, je to opravený :) – https://github.com/…c56946b5b27c. Průšvih s tou validací byl v tom, že při odeslání formuláře se spustí dva requesty, tlačítkový i formulářový, a každý z nich spouští Nette validaci. Doteď ale vrácení FALSE znamenalo pouze „nepůjde o ajaxový request“. Což není to, co v tu chvíli očekáváme, proto jsem pro tuto situaci přidal vynucené zavolání e.stopImmediatePropagation() a e.preventDefault(), což je ekvivalentní vrácení FALSE přímo v handleru eventu. Viz http://api.jquery.com/on/#…

Teoreticky je to změna chování, ale myslím, že by to nikoho nemělo ovlivnit.

Ideální by bylo, kdyby netteForms.js zanechaly na formuláři nějaký příznak, že validací neprošel. Pak by šlo jednoduše přihlédnout k této hodnotě, AJAX by se zrušil a mohly by doběhnout ostatní navěšené handlery všeho druhu. Teď abych zjistil, jestli formulář Nette validací projde, musím ji ručně zavolat, a pokud je netteForms.js přilinkován před nette.ajax.js, tak se validační rutina volá minimálně dvakrát. Ideálně řešení tedy ještě čeká, současné řešení beru jako dirty hack.

Editoval vojtech.dobes (8. 6. 2012 2:00)

uestla
Backer | 796
+
0
-

Super, moc díky – jak za opravu, tak za info o novém navěšování eventů, neměl jsem o tom ponětí.

22
Člen | 1478
+
0
-

@vojto: z toho se pomale stává master piece. Ja to mám nasazené už na 2 projektech, ovšem nikdy ne v kombinaci s netteForms.js. Jinak to chodí skvěle a pojetí se mi hodně líbí.

<ot>ten spinner od @uestla, jestli jsem to správně pochopil, tak je to drzá extension, která si vyrobí spinner, který se pak přes CSS nebo JS dostyluje. Líbilo by se mi víc, poslat do extension jen jQuery selector spinneru, sice je v extension proměnná spinner, ale s ní stejně metoda createSpinner nepočítá. Takže bych to udělal víc obecnější, protože u jednoho AAJXu můžu mít spinner vedle submitu a u druhého třeba jako dialog nebo jsem nepochopil, jak spinner jednoduše donutit chovat se v jedné aplikaci různě</ot>

Editoval 22 (9. 6. 2012 0:04)

uestla
Backer | 796
+
0
-

<ot>jak to myslíš „ode mě“? ^^</ot>

22
Člen | 1478
+
0
-

@uestla: promiň, nějak jsem si blbě zafixoval commit se tvým jménem a myslel jsem, že spinner extension je celá tvoje.

Editoval 22 (9. 6. 2012 1:07)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Vydávám 2 verze/verzičky.

1.0.1

  • doplněn chybějící výchozí selektor pro obrázková tlačítka se třídou ajax
  • opraveno získání souřadnic kliku na obrázkové tlačítko v kontejneru
  • opravena kompatibilita s netteForms.js (neprojití validace nyní zabije originální event objekt)

Díky za příspění @uestla.

1.1.0 (navazuje na 1.0.1)

  • s rozšířeními lze nyní manipulovat i po volání $.nette.init() (přidávat, vypínat, získávat pro úpravy)
  • u rozšíření nelze manipulovat s callbacky na události (viz níže)

Rozšíření se skládá ze sady callbacků a případného sdíleného kontext objektu. Callbacky de facto představují jádro rozšíření. Jejich úpravy/nahrazování mohou snadno vést k naprosté změně smyslu rozšíření – proto jsem se rozhodl zúžit API rozšíření na jejich kontext. Rozšíření jej mohou využívat k definování svého public rozhraní, zároveň by měla být psána tak, aby potřeba změny jednoho z callbacků vedla spíše k napsání vlastního nového rozšíření. Metoda $.nette.ext('name') nyní vrací přímo kontext extenze.

Budu rád za komentáře k této změně.

Extenze diagnostics.dumps

Do masteru jsem přidal rozšíření umožňující zobrazovat výsledky barDump volání proběhlých v Ajaxovém request přímo v DebugBaru. Stačí si do BasePresenteru přidat následující kód:

protected function afterRender()
{
	if (Debugger::isEnabled() && Debugger::$bar) {
		$panels = Nette\Reflection\ClassType::from(Debugger::$bar)
			->getProperty('panels');
		$panels->setAccessible(TRUE);
		$panels = $panels->getValue(Debugger::$bar);
		$this->payload->netteDumps = $panels['Nette\Diagnostics\DefaultBarPanel-4']->data;
	}
}
LeonardoCA
Člen | 296
+
0
-

Pěkný! Testuju tuto podporu ajaxu od včerejška.

Jeden dotaz, neměl jsem čas to domyslet. Bylo by možné zachytit klasický response 500 a zobrazit uživateli hlášku o chybě? Nezkoušel jsem jak se to chová v production modu, ale v development modu se mi nezobrazilo nic.

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

K dispozici je event error, takže nejlepší bude napsat si nějakou extenzi. Callback dostává stejné parametry jako callback na fail v klasickém jQuery ajaxovém requestu.

Editoval vojtech.dobes (15. 6. 2012 3:48)