nette.ajax.js – alt. obsluha pro AJAX s jQuery
- LeonardoCA
- Člen | 296
Experimentálně.
(function($, undefined) {
/**
* Shows HTTP Error Statuses from Ajax request
*/
$.nette.ext('httpStatusToFlash', {
error: function (payload) {
if (payload.status != 200) {
$('#snippet--flashMessages').html('<div class="alert alert-error"><a class="close" data-dismiss="alert">×</a>'+payload.status+' '+payload.statusText+'</div>');
}
}
});
})(jQuery);
A ještě více experimentálně https://gist.github.com/2946969 s odchycením headers pro FireLogger a zobrazením chyby do flash zprávy. Funguje i při neaktivním fireloggeru, protože extension posílá v send requestu vlastní header fireloggeru,
V produkčním režimu se zobrazí pouze „500 Internal Server Error“.
V development se zobrazí takhle (s použitím twitter bootstrap):
Odkaz je klikatelný pro otevření souboru v editoru.
Má to daleko k použitelné verzi, ale zatím jsem chtěl hlavně, ať se mi zobrazí nějaké info o chybě i při zavřeném firebugu.
Editoval LeonardoCA (18. 6. 2012 8:23)
- Vojtěch Dobeš
- Gold Partner | 1316
LeonardoCA To je přesně ta cesta, kterou bych se rád vydal :). Veliké díky za odvedenou práci – pokud nejsi proti, mohl bys práci v tom gistu otevřít jako pull request? Bude to na očích a naopak do toho pull request je možné otevírat další pull requesty.
- LeonardoCA
- Člen | 296
Mám to přidat jako extension do hlavního repozitory na github? S gitem začínám, takže v tom ještě plavu… nevím co přesně chceš. I proto se mi včera povedlo commitnout bez komentáře… psal jsi něco o pull requestu ze specifické branche – to si nejsem jist co tím myslíš? Budu si muset přečíst nějakou dokumentaci :)
Jinak hlavní věc co by to chtělo přídat, je předávat, element pro flash snippet a obalovací elementy jako parametry. Pak už by to mohl využít kdokoli.
Původně jsem zkoušel nějak hacknout debugger, aby poslal laděnku přímo html nebo json, ale ono to tady není jak udělat bez přímého zásahu do kódu. (Ale i tak, než složitě dekódovat výstup pro fireLogger by se hodilo, kdyby byl Debugger rozšířený o JSON výstup)
- Honza Marek
- Člen | 1664
Nette při pětistovce přidává hlavičku cca „X-Nette-Error-File“. Přece by šlo jednoduše udělat window.open(obsah hlavičky) a byla by otevřená laděnka v novém tabu.
- LeonardoCA
- Člen | 296
Tak to mi nedošlo, musím hned vyzkoušet…
Edited:
čas byl až teď.
Dá se využít „X-Nette-Error-Log“, akorát je škoda, že se neposílá
firelog info i tento header zároveň.
Osobně by mi stačilo na hloupé chyby a překlepy zobrazit jen základní info
a nepotřebuji k tomu celou laděnku.
A naopak při otevřeném firebugu se zase název souboru s logem
neposílá.
Jinak stačí přidat do base prezenteru něco jako
public function handleGetExceptionLogFile() {
echo file_exists($filename = $this->getParam('file')) ? file_get_contents($filename) : '"'.$this->getParam('file').'" not found.';
$this->terminate();
}
a v js pak pří chybě
var fileLog = payload.getResponseHeader('X-Nette-Error-Log');
if (fileLog) {
matches = window.location.href.match(/^[^\?#]*/g);
var getLogUrl = matches[0] + '?do=GetExceptionLogFile&file='+fileLog;
// displays debugging info in iframe
html += '<iframe src="'+getLogUrl+'" width="' + (flashMessages.width() - 30) + '" height="500"></iframe>';
// or opens new window with debugging info
window.open(getLogUrl);
}
zatím jen proof of concept, určitě to půjde udělat pěkněji
Editoval LeonardoCA (18. 6. 2012 21:32)
- LeonardoCA
- Člen | 296
Ještě dotaz, máte někdo dořešenou situaci kdy, expiruje session a uživatel je odhlášen a on odešle přes ajax formulář nebo klikne na nějaký ajaxový odkaz?
Ještě se mi to nepodařilo uspokojivě dořešit, v SecurePresenteru používám.
$this->redirect('Auth:default', array('backlink' => $this->getApplication()->storeRequest('+1 minutes')));
a po přihlášení
$presenter->getApplication()->restoreRequest($presenter->backlink);
ale při použití ajaxu to stále nefunguje správně
Uvažuju ještě o možnosti místo přesměrování uživateli rovnou zobrazit přihlašovací formulář a po úspěšném přihlášení rovnou pokračovat v akci, ale to taky není úplně triviální, aby to fungovalo jak pro všechny formuláře, tak pro odkazy a přemýšlím, jak to šikovně realizovat pomocí nette.ajax.js extension.
- stefi023
- Člen | 71
Zdravim,
LeonardoCA napsal(a):
public function handleGetExceptionLogFile() { echo file_exists($filename = $this->getParam('file')) ? file_get_contents($filename) : '"'.$this->getParam('file').'" not found.'; $this->terminate(); }
tohle zobrazovani chybovych hlasek pri ajaxu je prima, preci jen vic kamaradim s ladenklou nez z fireloggerem. Jen by me zajimalo, jak byste u tohohle nejlepe resili zabezpeceni? Aby se nikdo nedostal k souborum, na ktere nema narok?
Nejak ho „jailnout“ napr jen do log adresare, plus pripadne nejaka
podminka isDevelopmentMode()
nebo uplne jinak a lepe?
Podobnou funkcionalitu bych si totiz rad zprovoznil na cteni zalogovanych chyb z debug panelu, protoze kolikrat na stroj, kde vyvijim nemam pristup na primo (pouze pres http) a lezu tam pres ssh pres nekolik ruznych stroju, proto jednoduche zobrazeni a pripadne stazeni vygenerovanych erroru je celkem opruz.
- LeonardoCA
- Člen | 296
Dobrá poznámka. Zabezpečení a celkově jsem to ještě nedomyslel. Určitě by se muselo ošetřit.
- podmínka pro development mód
- předávat jen jméno souboru a ne celou cestu (omezení tak bude automaticky jen na adresář logů)
a možná ještě úplně nějak jinak
Myslím že DG někde tady psal, že plánuje zobrazování logů přidat přímo do nette (ale nebude to hned)
jak to budu ladit pro sebe, budu se snažit udělat to jako addon a nemuset nic dávat do presenteru, případně to nějak napojím na debug bar (nebude to hned :-)
- Vojtěch Dobeš
- Gold Partner | 1316
<možná není nutné>
Pokud se nemýlím, nejde to :).
To je výborný usecase… buď bych to řešil přidáním veřejné metody do
kontextu snippets
extenze, anebo udělal obecný mechanismus
zavolání extenze ručně. Nevím, co je
lepší.</možná není nutné>
Nicméně ještě se dívám na tvůj příklad: pokud jde de facto
o ajaxový požadavek, kde by ti v ideálním případě nevadilo (ale naopak)
využít dobrodiní všech registrovaných extenzí, tak můžeš místo
$.get(...
použít metodu $.nette.ajax
:
$.nette.ajax({
type: 'get',
url: '?do=fbLogin'
});
První argument je de facto pole options
pro klasickou
$.ajax()
. Metoda přitom projede i událostní cyklus
nette.ajax.js
(automatické bindování de facto jen binduje
volání této metody).
Editoval vojtech.dobes (21. 6. 2012 12:49)
- Vojtěch Dobeš
- Gold Partner | 1316
Buď přes extension, anebo ve tvém případě lze rovnou:
$.nette.ajax({
type: 'get',
url: ...
}).done(function (payload) {
...
});
Editoval vojtech.dobes (21. 6. 2012 20:49)
- 22
- Člen | 1478
no právě tohle s předešlým až tak nesouvisí. Potřeboval bych něco:
$.nette.init({
success: function(){
myFunction();
}
})
Takže jsem asi předpokládal správně extension
, protože
default success
jinak nijak nerozšířím, že?
Jen se ptám, abych nedělal něco složitěji, než to jde, když se dá volat
například $.nette.ext('init').linkSelector
, tak by se mi
libílo $.nette.ext('init').success = function(){}
Editoval 22 (21. 6. 2012 21:17)
- Vojtěch Dobeš
- Gold Partner | 1316
Jo, tak v globálním případě jsou právě na to extenze.
$.nette.ext('foobar', {
success: function (payload) {
myFunction();
}
});
- Vojtěch Dobeš
- Gold Partner | 1316
Zrovna $.nette.ext('init')
pro událost success
nic
nedefinuje – naprostá většina funkcionality je rozsekaná do jednotlivých
extenzí, které lze vypínat.
- LeonardoCA
- Člen | 296
přidej si do payload nějaký příznak a v sucess podmínku pokud je nastaven tak zavoláš callbacky, podobně jako je uděláno redirect atp.
- 22
- Člen | 1478
@hosiplan: to mě napadlo, ale nějak to nefunguje nebo něco píšu blbě, po6adavek se odešle, ale callback formuláře se neprovede, teda aspoň podle FireLoggeru, který má vypsat data a nevypíše.
$('.test').on('click', function(){
var form = $(this).closest('form');
$.nette.ajax({
type: 'POST',
url: form.attr('action'),
data: form.serialize(),
success: function(){
alert('done');
}
});
return false;
})
Editoval 22 (26. 6. 2012 9:24)
- stefi023
- Člen | 71
22 napsal(a):
Pokud tim callbackem myslis toto:
success: function(){ alert('done'); }
tak to by teoreticky fungovat melo – zkousel jsem si obdobny pripad (kde
jsem pouzival volani $.nette.ajax()
) a alert mi probehl.
ale jinak myslim ze @LeonardoCA mel na mysli si
napsat vlastni extenzi podobnou te redirect
:
$.nette.ext('callbacks', {
success: function (payload) {
if (payload.callbacks) {
for (var i in payload.callbacks) {
eval(payload.callbacks[i]);
}
}
}
});
a v presenteru neco jako:
$this->payload->callbacks = array(
"alert('done');",
);
Netestovano – psano z hlavy :( a mozna to take neni uplne koser takhle
predavat js kod a provadet eval()
, zalezi na tom jak moc chces tu
extenzi univerzalni… jinak bych si napsal nejakou konkretni extenzi a posilal
jen jeji „parametry“
Dokazu si treba hezky predstavit extenzi focus:
$.nette.ext('focus', {
success: function (payload) {
if (payload.focus) {
$('#'+payload.focus).focus();
}
}
});
- stefi023
- Člen | 71
@22: Tak ted ti asi uplne nerozumim… jako ze neprobehne ten ten POST na formulari? dle jquery dokumentace by success mel byt volan pouze pokud ajax probehne:
success(data, textStatus, jqXHR)
A function to be called if the request succeeds.
//EDIT:
nebo ti ajax probehne a neprobehne ten naveseny callback na tlacitku
forumlare (onClick[]
)? Pak bych bych videl problem v tom, ze se ti
neposila ajaxem, kttere tlacitko „bylo zmacknuto“
rekl bych ze se to resi nekde tady: nette.ajax.js:241
//EDIT2:
jinak tyhlety pripady resim tak (i kdyz nevim zda spravne – ale funguji), ze
tlacitku (nebo celemu formulari – podle toho zda chces zajaxovat pouze to
tlacitko ci cely formular) dam tridu „ajax“ a pak uz jen na tlacitku udelam
trigger('click.nette')
a provede se „vsechno co je
treba“ samo:
$('selektorTlacitka').trigger('click.nette');
Editoval stefi023 (26. 6. 2012 10:28)
- 22
- Člen | 1478
@stefi: tvé odpovědi moc nerozumím.. můžeš mi napsat tedy
k tomuhle obsluhu, tak, aby proběhl callback pro onClick[]
protected function createComponentMyForm()
{
$form = new Form();
$form->addText('value', 'Value:');
$form->addImage('ok', 'pathToImage', 'OK')
->onClick[] = callback($this, 'ajaxSubmit');
$form['ok']->getControlPrototype->class('myAjax');
return $form;
}
public function ajaxSubmit(SubmitButton $form)
{
Debugger::fireLog($form->form->values);
}
edit: a pak nějaký tedy vlastní JS callback v success metodě.
Editoval 22 (26. 6. 2012 11:01)
- stefi023
- Člen | 71
22 napsal(a):
@stefi: tvé odpovědi moc nerozumím.. můžeš mi napsat tedy k tomuhle obsluhu, tak, aby proběhl callback pro
onClick[]
Predne teda asi getControlPrototype()
je metoda takze se
zavorkami (ale to byl asi jen preklep)
Pres extenzi
$form['ok']->getControlPrototype()->class('myAjax ajax');
//$form['ok']->getControlPrototype()->addClass('myAjax ajax'); //asi lepsi, co kdyz uz tam neco mas
a pak
$.nette.ext('callbacks', {
success: function (payload) {
if (payload.callbacks) {
for (var i in payload.callbacks) {
eval(payload.callbacks[i]);
}
}
}
});
Potom
$('.myAjax').trigger('click.nette');
a
public function ajaxSubmit(SubmitButton $form)
{
$this/*->presenter*/->payload->callbacks = array("alert('done');")
Debugger::fireLog($form->form->values);
}
Pres $.nette.ajax()
pak by asi mohlo stacit:
$('.myAjax').on('click', function(){
var form = $(this).closest('form');
var values = form.serialize();
values[$(this).attr('name') = $(this).val();
$.nette.ajax({
type: 'POST',
url: form.attr('action'),
data: values,
success: function(){
alert('done');
}
});
return false;
})
//opet netestovano
Editoval stefi023 (26. 6. 2012 11:08)
- Vojtěch Dobeš
- Gold Partner | 1316
$.nette.ajax()
volá extenze úplně stejně jako automatické
navěšení. Tzn. že lze buď využít:
$.nette.ajax({ ... }).done(function (success) {
alert('done');
});
Nebo (což se použije pro všechny ajaxifikované požadavky):
$.nette.ext('alertDone', {
success: function (success) {
alert('done');
}
});
Co nejdřív o tom napíšu trochu podrobnější českou dokumentaci.
- Vojtěch Dobeš
- Gold Partner | 1316
Ad 22: jop, máš samozřejmě pravdu :) Plugin by snad potřeba upravovat nemělo být nikdy – je lepší vypnout a vytvořit vlastní (nebo jen vytvořit vlastní :) ) – což je výhoda při aktualizacích.
Editoval vojtech.dobes (26. 6. 2012 14:06)
- 22
- Člen | 1478
@stefi023: už nevím, jak to mám říct:
$('.myAjax').on('click', function(){
var form = $(this).closest('form');
var values = form.serialize();
values[$(this).attr('name')] = $(this).val();
$.nette.ajax({
type: 'POST',
url: form.attr('action'),
data: values,
success: function(){
alert('done');
}
});
return false;
})
…ani tohle tvoje řešení prostě nezavolá PHP onClick[] callback.. Takže se ptám znova, jak odeslat jeden form jinak? Extension se mi na to psát nechce kvůli jednomu formuláři.
- Vojtěch Dobeš
- Gold Partner | 1316
Pro správné chápání: metoda $.nette.ajax()
je ekvivalentem
$.ajax()
– ta sama o sobě taky neumí vyčarovat kouzla jako
rozpoznání odesílacího tlačítka. Nicméně $.nette.ajax()
na
takové případy pamatuje :):
$('.myAjax').on('click', function (e) {
$.nette.ajax({}, this, e).done(function () {
alert('done');
});
});
Klíčové je předání druhého a třetího argumentu metodě
$.nette.ajax()
, které umožní skriptu automaticky provést
validaci události, zpracovat formulářové hodnoty atd. jako při automatické
ajaxifikaci.
Editoval vojtech.dobes (26. 6. 2012 18:58)
- Vojtěch Dobeš
- Gold Partner | 1316
@stefi023 Taky jsem si říkal, proč tebou uvedené řešení @dvacetdvojce nefungovalo, ale asi to bylo tím, že šlo o obrázkové tlačítko, které se zpracovává zase trochu jinak.
Editoval vojtech.dobes (26. 6. 2012 23:39)
- LeonardoCA
- Člen | 296
vojtech.dobes:
Extenze diagnostics.dumps
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; } }
zkusil jsem to přepsat s použitím Application->onRequest
Application->onResponse aby se nemuselo nic přidávat do prezenteru a dalo
se to zakomponovat přes compilerExtension
https://gist.github.com/3107582
Pohrávám si s myšlenkou, že by se to dalo napsat obecně pro libovolný panel.
Otázky do placu:
1. Pro které panely to má/nemá smysl?
- mi chybí hlavně aktualizace dibi panelu (proto jsem s tím začal, ale bylo by zbytečné to dělat pro každý panel zvlášť pokud by se to hodilo pro více panelů)
2. Možná by byl dobrý volitelný režim append/replace, ne vždy mi vyhovuje, že se data skládají za sebe a pak musím scrolovat
3. Nějaké další nápady?
Editoval LeonardoCA (14. 7. 2012 12:57)
- LeonardoCA
- Člen | 296
Momentálně funkční, ale ještě těžkopadné řešení
https://gist.github.com/3107582
Integroval jsem pomocí compiler extension
- barDump při ajaxu
- na stejném principu dibi panel při ajaxu
- zobrazení laděnky do iframu při ajaxu
vše se aktivuje jen přilinkováním javascriptu a přidáním compilerExtension do bootstrapu
$configurator->onCompile[] = function ($configurator, $compiler) {
$compiler->addExtension('ajaxpanels', new Lxo\Ajax\AjaxPanelsCompilerExtension);
};
Zatím se mi nepodařilo žádným způsobem jednoduše přidat obsluhu signálu do prezenteru, nakonec jsem to vyřešil nepěknou oklikou, přes Application->OnError[], protože se mi nepodařilo žádným způsobem ani přidat handle, ani přidat komponentu do presenteru.
Nejvíce by se mi líbilo, kdyby se dalo použít $presenter->AddComponent(), ale prezenter nemá žádné eventy, které by se daly použít a application nemá žádný event mezi vytvořením prezenteru a $presenter-run()
Chci moc, když nechci aby kdejaký addon kvůli každé maličkosti potřeboval přidávat něco do BasePresenteru? Nebo je na tom jak to chci dělat koncepčně něco špatně?
- Filip Procházka
- Moderator | 4668
Já bych řekl, že ti musí stačit Application::$onRequest. Nevidím tam nic, na co bys potřeboval presenter. V request objektu máš všechny potřebný parametry.
A měl by sis říkat pouze o hash chyby, nevidím důvod proč by se muselo přenášet celý název souboru.
- LeonardoCA
- Člen | 296
Já bych řekl, že ti musí stačit Application::$onRequest. Nevidím tam nic, na co bys potřeboval presenter. V request objektu máš všechny potřebný parametry.
Díky, to jsem potřeboval – pro tento případ – kdy nepotřebuju vyloženě presenter (už jsem se v tom včera trochu motal..)
Ale co kdybych potřeboval/chtěl do presenteru opravdu přidat nějakou komponentu?
A měl by sis říkat pouze o hash chyby, nevidím důvod proč by se muselo přenášet celý název souboru.
To ještě budu refaktorovat. Ale z hlediska bezpečnosti to není problém, protože to funguje jen v debug módu. A celé názvy souborů jsou stejně v laděnce všude.
Editoval LeonardoCA (15. 7. 2012 10:14)
- MartinitCZ
- Člen | 580
Chtěl jsem se zeptat. Je možné něaco takového?
// Ajax submit
$.nette.init(function(ajaxHandler) {
$("a.ajax").live("click", ajaxHandler);
// $("form.search input.submit").live("click", ajaxHandler);
$("form.search input.name").live("keyup", function() {
if ($(this).attr("value").length >= 3) {
console.log("ok"); // Zde odeslat
}
});
- jsem narazil na problém s validací formu.
Form je ve snippetu content a když se celý kontent „refresne“ po odeslání ajaxem, tak nejde js validace. Jak toto opravit?
- Vojtěch Dobeš
- Gold Partner | 1316
Oprava validace:
$.nette.ext('form-validation', {
success: function () {
$('form').each(function () {
Nette.initForm(this);
});
}
});
První příklad:
$.nette.ext('foobar', {
load: function () {
$("form.search input.name").on('keyup', function () {
if ($(this).attr('value').length >= 3) {
console.log('ok'); // Zde odeslat
}
});
}
});
Pokud bys chtěl v tom okamžiku přímo odeslat ten formulář (tedy de
facto odeslat jej při události keyup
za jisté podmínky…), tak
by to vypadalo takto:
$.nette.ext('foobar', {
load: function () {
$("form.search input.name").on('keyup', function (e) {
var $this = $(this);
if ($this.attr('value').length >= 3) {
$form = $this.closest('form');
$.nette.ajax({
validate: {keys: false}
}, $form[0], e); // Zde odeslat
}
});
}
});
Editoval vojtech.dobes (19. 7. 2012 19:42)
- Jan Mikeš
- Člen | 771
Rad bych se zeptal, jak to resit, kdyz jsem si trosicku upravil extension snippets metodu applySnippet na:
<script>
applySnippet: function ($el, html) {
// original
// $el.html(html);
$el.fadeTo(200, 0.01, function () {
$(this).html(html).fadeTo(500, 1);
});
},
</script>
Mam problem s tim, ze potrebuji volat funkci po provedeni ajaxu, viz:
<script>
$.nette.ext('done', {
complete: function (success) {
setTimeout(startup, 201);
},
before: function(event){
$("#ajax-spinner").show();
}
});
</script>
Nenapada vas nekoho lepsi reseni nez pres setTimeout? Kdyz totiz 20× kliknu na ajaxovy odkaz, tak se obcas stava, ze se funkce neprovede. A pokud si volani funkce narvu do callbacku k fadeTo, tak to je zase pozde, protoze se provede az po zviditelneni obsahu a ten pak obcas skace (ve funkci startup toho mam mraky).
Jak pripadne jinak a lepe vyresit update snippetu s efektem viz. https://componette.org/search/?… a zkombinovat to s volanim vlastni funkce po provedeni efektu?
- Kurtas
- Člen | 109
Muze mi prosim nekdo poradit jak docilit toho aby mi nette.ajax.js nemenil URL po vykonani ajax requestu? Meni mi URL dle HREF nekdy je to prospesne (treba strankovani) ale nekdy je to naopak kontraproduktivni. Urcite to jde resit nejak jednoduse, ale bohuzel si nevim rady :(
Predem moc dik!
- llook
- Člen | 407
Kurtas napsal(a):
Muze mi prosim nekdo poradit jak docilit toho aby mi nette.ajax.js nemenil URL po vykonani ajax requestu? Meni mi URL dle HREF nekdy je to prospesne (treba strankovani) ale nekdy je to naopak kontraproduktivni. Urcite to jde resit nejak jednoduse, ale bohuzel si nevim rady :(
Predem moc dik!
Můžeš vypnout celý history plugin:
<script type="text/javascript">
$.nette.ext('history', false);
</script>