nette.ajax.js – alt. obsluha pro AJAX s jQuery
- Vojtěch Dobeš
- Gold Partner | 1316
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
- Jan Tvrdík
- Nette guru | 2595
Mít hotový nový addon portál, dal bych ti like!
Návrh na zlepšení: Zkus ten kód okomentovat pomocí jsDoc.
- Vojtěch Dobeš
- Gold Partner | 1316
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
Jinak snad se mi podaří to celé rozložit ještě víc :). Rád bych i tuhle kontrolu vyčlenil jako extension.
- Vojtěch Dobeš
- Gold Partner | 1316
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
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.
- action formuláře musí vést na cílové URL ⇒ Továrnička na formulář se musí nacházet i na výsledné URL.
- V historii zůstává v URL GET parametr
do
.
- uestla
- Backer | 799
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
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.
- Vojtěch Dobeš
- Gold Partner | 1316
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
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
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.
- Vojtěch Dobeš
- Gold Partner | 1316
Přidána podpora pro odesílání souřadnic kliknutí v obrázkovém tlačítku (thx to @uestla).
- na1k
- Člen | 288
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
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
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
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í:settings
,ui, `e
- bojovyletoun
- Člen | 667
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
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 | 799
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
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
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);
});
}
}
})
- 22
- Člen | 1478
@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
Já ti už ani nevím, na co je to .livequery dobré :)
To je slovo :)
- uestla
- Backer | 799
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 | 799
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
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
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)
- 22
- Člen | 1478
@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)
- 22
- Člen | 1478
@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
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
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
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)