nette.ajax.js – alt. obsluha pro AJAX s jQuery
- tatyalien
- Člen | 239
Prosím nekamenujte mě, na ajax jsem poleno. Rozchodil jsem si ho nedávno na jednom projektu (jquery.nette.js, jquery.ajaxform.js) a chtěl jsem ho vyměnit za tento plugin (nette.ajax.js). Ale nechápu jak si tam zapnu třeba zobrazení spinneru, upravení odkazů na ajax.
V minulém ajaxu jsem používal:
jQuery.ajaxSetup({
cache: false,
dataType: 'json',
success: function (payload) {
if (payload.snippets) {
for (var i in payload.snippets) {
$('#' + i).html(payload.snippets[i]);
}
}
}
});
$(function () {
// vhodně nastylovaný div vložím po načtení stránky
$('<div id="ajax-spinner"></div>').appendTo("body").ajaxStop(function () {
// a při události ajaxStop spinner schovám a nastavím mu původní pozici
$(this).hide().css({
position: "fixed",
left: "50%",
top: "50%"
});
}).hide();
});
// odesílání odkazů + stránkování
//$('a.ajax').live('click', function (event) { // bez ajaxu (paginator)
// s ajaxem (paginagor)
$('a.ajax, .paginator a').live('click', function (event) {
event.preventDefault();
$.get(this.href);
// zobrazení spinneru a nastavení jeho pozice
$("#ajax-spinner").show().css({
position: "absolute",
left: event.pageX,
top: event.pageY
});
});
// odesílání formulářů
$('form.ajax').live('submit', function (event) {
event.preventDefault();
$.post(this.action, $(this).serialize());
// zobrazení spinneru a nastavení jeho pozice
$("#ajax-spinner").show().css({
position: "absolute",
left: $(this).position().left + 200,
top: $(this).position().top - 60
});
});
$('form.ajax :submit').on('click', function(event) {
event.preventDefault();
$(this).ajaxSubmit();
// zobrazení spinneru a nastavení jeho pozice
$("#ajax-spinner").show().css({
position: "absolute",
left: $(this).position().left + 200,
top: $(this).position().top - 60
});
});
Ve stránce mám nalinkováno: jquery.1.7.min.js, netteForms.js, nette.ajax.js, diagnostics.dumps.ajax.js, scrollTo.ajax.js, spinner.ajax.js, example.js
- Kurtas
- Člen | 109
Pro spinner ⇒ nalinkuj si z adresare etensions soubor spinner.ajax.js a spinner se bude ukazovat (ale nastyluj si div #ajax-spinner a taky vycentruj :) )
nette.ajax.js defaultne obaluje vsechny odkazy co maji classu ajax pokud to chces zmenit / neco pridat tak pred nette.init() zadej toto (zrovna toto je v readme)
<script>
$.nette.ext('init').linkSelector = 'a.ajax, .paginator a';
$.nette.init();
</script>
- tatyalien
- Člen | 239
tak paginator fachá, ale ten spinner ne ;)
v css mám
#ajax-spinner {
margin: 0 0 0 0;
min-width: 31px;
min-height: 31px;
background: yellow url('../images/ajax-loader.gif') no-repeat 50% 50%;
font-size: 0;
z-index: 123456;
display: none;
}
a nalinkovanej extension spinner pred spustenim $.nette.init().
- Vojtěch Dobeš
- Gold Partner | 1316
Zkusil bych odstranit display: none;
:) ze základního
stylopisu. Extenze spinner pouze explicitně skrývá, nikoliv explicitně
zobrazuje.
- tatyalien
- Člen | 239
Díky, už se zobrazuje, ale pouze tam kde ho určím. V tom starém jsem měl:
$("#ajax-spinner").show().css({
position: "absolute",
left: $(this).position().left + 200,
top: $(this).position().top - 60
});
Že se vzala pozice kliknutí na ajaxový požadavek a dle té hodnoty se zobrazil v přibližné pozici kliknutí. Ale jak to mám tady nastavit ;)
- llook
- Člen | 407
tatyalien napsal(a):
Díky, už se zobrazuje, ale pouze tam kde ho určím. V tom starém jsem měl:
$("#ajax-spinner").show().css({ position: "absolute", left: $(this).position().left + 200, top: $(this).position().top - 60 });
Že se vzala pozice kliknutí na ajaxový požadavek a dle té hodnoty se zobrazil v přibližné pozici kliknutí. Ale jak to mám tady nastavit ;)
Já to teď tak nějak mám, pozicuju ho na event.pageX/event.pageY:
$.nette.ext('spinner', {
init: function () {
spinner = $('<div></div>', { id: "ajax-spinner" });
spinner.appendTo("body");
},
before: function (settings, ui, e) {
$("#ajax-spinner").css({
visibility: "visible",
left: e.pageX,
top: e.pageY
});
},
complete: function () {
$("#ajax-spinner").css({
visibility: "hidden"
});
}
});
A takto v CSS:
#ajax-spinner {
visibility: hidden;
position: absolute;
width: 14px;
height: 14px;
padding: 3px;
background: white url('../images/spinner.gif') no-repeat 50% 50%;
z-index: 123456;
}
Úplně spokojený s tím nejsem. Hlavně proto, že mám pouze jeden spinner a to u posledního ajaxového kliku. To je nežádoucí, pokud uživatel kliká rychleji, než aplikace odpovídá…
- Vojtěch Dobeš
- Gold Partner | 1316
Ve výchozím nastavení nette.ajax.js
nechává běžet vždy
jen jeden request zároveň. Lze to vypnout pomocí
$.nette.ext('unique', false);
, a pak je ten vícenásobný spinner
třeba řešit.
- Kurtas
- Člen | 109
Ahoj, můžeme prosím někdo poradit s následujícím problémem:
Na jedné stránce mám několik záznamů a u každé mám řádek s „Actions“ a po kliknutí potřebuji zavolat ajax a na success rozbalit actionBody ovšem při druhém kliku na „Actions“ nechci volat ajax ale je zabalit actionBody, třetí klik zase vola ajax a rozbaluje a tak stale dokola…
<?php
//sablona - probiha ve smycce
<div class="actionHead"><a href="{link Portfolio:getAction, $type, $id}" class="ajax">Actions</a></div>
<div class="actionBody">
{snippet actionBody}{/snippet}
</div>
?>
Zkousel jsem toto, ale alert okno se vubec neukaze (zkousel jsem load i init methody)
<script>
$.nette.ext('actionExpander',{
load: function () {
$(".actionHead").livequery(function() {
$(this).on('click',function(e){
this.ui = $(this);
alert('KLIK'); //Zde volat AJAX
});
});
},
success: function(payload){
this.ui.next(".actionBody").slideToggle(600);
this.firstClick = false;
}
},{
firstClick: true,
ui: null
}
);
</script>
Moc díky za pomoc
- Vojtěch Dobeš
- Gold Partner | 1316
Uvedený kód má jednu chybu z hlediska Javascriptu: funkce předaná
metodě livequery()
ma jiný kontext, než kód, který tuto metodu
volá (a tedy jiný, než je kontext rozšíření. Nicméně
livequery()
je jednak zastaralé, druhak by vůbec nemělo být
potřeba. Tedy:
$.nette.ext('actionExpander', {
load: function () {
var _this = this;
$('.actionHead').on('click', function (e) {
var $this = $(this);
if (!$this.attr('data-expander')) {
$this.attr('data-expander-called', true);
_this.req = $.nette.ajax({}, $this.find('a')[0], e).done(function () {
$this.next('.actionBody').slideToggle(600);
_this.req = null;
});
} else {
if (_this.req) {
_this.req.abort();
}
$this.attr('data-expander-called', null);
$this.next('.actionBody').slideToggle(600);
}
});
}
}, {
req: null
});
Snad to není moc mimo od původního zadání.
- Kurtas
- Člen | 109
Vojto diky za pomoc ale pokud si dam alert za event co ma hlidat kliknuti na div s class actionHead tak se mi to alert okno stejne nezobrazi :( tzn vubec nic se nevykonava
<script>
$.nette.ext('actionExpander', {
load: function () {
var _this = this;
$('.actionHead').on('click', function (e) {
alert('click');
var $this = $(this);
.....
....
</script>
- Vojtěch Dobeš
- Gold Partner | 1316
A když dáš to bindování jen do $(function () { ... });
,
tak alert vyskočí? Pokud ne, tak to nesouvisí s ajaxem.
- Kurtas
- Člen | 109
jj tak to vyskoci … nevim jesli to dobre chapu ale kdyz mam alert navrcholu
v load methode
tak by me mel vyskocit ihned po nacteni stranky (i bez jakehokoliv ajax
pozadavku) nebo ne? Me nevyskakuje … Kdy dojde ke zpracovani load metody a tim
padem zaregistrovani bindovani?
<script>
$j.nette.ext('actionExpander', {
load: function () {
alert('Nette.ajax.js load');
var _this = this;
......
......
</script>
Editoval Kurtas (5. 8. 2012 11:35)
- Jakub Šulák
- Člen | 222
Zdravím,
již pár dní se mořím s jedním problémem:
Snažím se načítat data u stránkování pomocí tlačítka Dalších X,
tedy nepotřebuji obsah snippetu nahradit, ale provést append.
Tedy je nutné vypnout .ext(‚snippet‘)
<script>
$.nette.ext('snippets',false);
$.nette.ext('scrollTo',false);
$.nette.ajax({
url : 'url?page=1'
}, this).done(function(payload){
var s = payload['snippets']['snippet--productsList'];
$(s).children('li').each(function(){
$(this).hide();
$(this).appendTo('#products-list');
})
$('#products-list li:hidden').fadeIn();
});
</script>
Nyní ale řeším problém, jak zase zapnout extensions. Je nějak možné ve funkci $.nette.ajax() nastavit, že se nemají použít dané extensions pouze pro tuto relaci.
Je to nějak možné?
- Vojtěch Dobeš
- Gold Partner | 1316
Tak jsem to zprovoznil ve větvi extensions-exclude
.
Použití:
$.nette.ajax({
url: 'url?page=1',
off: {
snippets: true
}
}, this).done(function (payload) {
var s = payload['snippets']['snippet--productsList'];
$(s).children('li').each(function () {
$(this).hide();
$(this).appendTo('#products-list');
});
$('#products-list li:hidden').fadeIn();
});
Prosím o vyzkoušení.
- Jakub Šulák
- Člen | 222
Prozkoušel jsem to, ale nefunguje to. Bohužel to teď nemám čas odkrokovat, tak jen kusé informace:
- oproti původnímu se nyní skutečně vizuálně append provede, ale následně se provede invalidace snippetu
- stále musí obsahovat $.nette.ext(‚snippets‘,false);
- zkusím pak odkrokovat
- ji_ri_k
- Člen | 44
Ahoj,
mám také problém s dvojí validací a nejsem schopen přijít na kloub proč
tomu tak je. Používám jQuery UI dialog do kterého načítám obsah. A ten
obsah může obsahovat Nette formuláře. A právě zde mi javascript
formuláře dvakrát validuje. Je mi jasné, že chyba pramení z mých
slabších znalostí Javascriptu a proto se na vás obracím.
Viz následující kód:
// nastaveni, inicializace nette.ajax
$.nette.ext('init').formSelector = 'form.ajax, .modal-window form';
$.nette.init(function (ajaxHandler) {
$('form.ajax, .modal-window form').live('submit', ajaxHandler);
});
// otevreni dialog okna a nacteni jeho obsahu
$("a.modal").on("click", function() {
var id = $('.modal-window').length + 1;
var href = $(this).attr('href');
$('body').append('<div class="modal-window" id="modal-' + id + '"></div>');
$("#modal-" + id).dialog({
open: function(event, ui) {
// po otevreni okna nacitam obsah z href (napr. Language:add)
// kde view add nema zadny laout (obsahuje jen napr. formular)
$("#modal-" + id).load(href, function() {
// inicializuji vsechny formulare v nove otevrenem
// dialogovem okne (bez ni se JS validace neprovede,
// ale s ni se provadi dvakrat)
$("#modal-" + id + ' form').each(function() {
Nette.initForm(this);
});
});
}
});
});
A šablona pro Language:add je např. takováto:
{layout '@modal.latte'}
{block content}
{control languageForm}
{* tzn. zobrazi se napr. jen formular *}
- ji_ri_k
- Člen | 44
Tak jsem to vyřešil prozkoumáním nette.ajax.cz a lze to udělat i bez pomocí live.
// nastaveni, inicializace nette.ajax
$.nette.ext('init').formSelector = 'form.ajax, .modal-window form';
$.nette.init(); // neni treba volat live
// otevreni dialog okna a nacteni jeho obsahu
$("a.modal").on("click", function() {
var id = $('.modal-window').length + 1;
var href = $(this).attr('href');
$('body').append('<div class="modal-window" id="modal-' + id + '"></div>');
$("#modal-" + id).dialog({
open: function(event, ui) {
// po otevreni okna nacitam obsah z href (napr. Language:add)
// kde view add nema zadny laout (obsahuje jen napr. formular)
$("#modal-" + id).load(href, function() {
// inicializuji vsechny formulare v nove otevrenem
// dialogovem okne (bez ni se JS validace neprovede,
// ale s ni se provadi dvakrat)
$("#modal-" + id + ' form').each(function() {
Nette.initForm(this);
});
$.nette.load(); // zde pridano nove nacteni
});
}
});
});
- Vojtěch Dobeš
- Gold Partner | 1316
Verze 1.1.2
- přidána podpora pro anonymní rozšíření (první argument
name
je nyní nepovinný), takže lze rozšíření přidávat i takto:
$.nette.ext({
load: function () { ...
});
- rozšíření lze vypnout na úrovni konkrétního requestu, nikoliv jen globálně
$.nette.ajax({
off: ['snippets']
}, this, e);
nebo
<a n:href="foo!" class="ajax" data-ajax-off="snippets">Do something.</a>
- přidána nová shortcut metoda, kterou lze volat nad jQuery objekty
$('#element').click(function (e) {
$(this).netteAjax(e, { ... }).done(function () { ... });
});
- skript nyní bere v potaz nastavení
setValidationScope(FALSE)
u formulářů (v1.1.1) - formuláře vrácené ve snippetu jsou nyní znovu automaticky doplněny o Nette validaci (není třeba řešit vlastním rozšířením)
- opraveno nativní rozšíření unique pro udržování pouze jednoho requestu v běhu
- klasyc
- Člen | 14
Ahoj chlapi,
velmi se mi líbí to, co tu tvoříte. Proto bych zde rád rozvinul diskuzi o novém rozšíření, které by umožňovalo snadnou práci s modálními formuláři. Nejde mi o konkrétní kód, ale spíš o přímočaré a čisté řešení.
Příklad: mám neajaxovou aplikaci, dejme tomu telefonní seznam. V tabulce jmen je na každém řádku tlačítko Upravit, kliknutím mě pošle na stránku „úprava jména“. Teď z té aplikace chci udělat aplikaci ajaxovou a místo stránky „úprava jména“ chci mít modální formulář obsahující tuto stránku.
Návrh řešení:
- Uzavřít editační formulář do snippetu a na ajaxové dotazy odpovídat pouze invalidovaným snippetem. Tím budu schopen stáhnout na požádání snippet s předvyplněnými údaji a umístit ho do modálního okna.
<?php
public function actionEdit($id) {
...
$this->getComponent("editForm")->setDefaults(array("name" => $oldName));
if ($this->isAjax()) {
$this->invalidateComponent("editForm");
}
?>
- Vyrobit extenzi
dialog
, která bude schopna vytvořit modální okno (například pomocí jQueryUI) a do něj plácnout libovolný snippet s formulářem. Formuláře obsažené v tomto dialogu se budou pochopitelně odesílat ajaxem. - Při vyhodnocení formuláře odeslaného ajaxem připojit do
$this->payload
informaci o tom, jestli se formulář zpracoval korektně a jestli se má dialog zavřít nebo zůstat otevřený a natáhnout formulář se zvýrazněnými chybami.
Něco podobného mi už funguje, ale nelíbí se mi, jak je to napsané. Toto je můj poslední nápad a budu vděčný za vaše připomínky.
Mějte se pěkně
Honza
Editoval klasyc (26. 8. 2012 0:23)
- Filip Procházka
- Moderator | 4668
V podstatě jsi si odpověděl sám :) Když si založíš nové vlákno a hodíš tam víc ukázek kódu, můžeme ti zkusit pomoct ho trošku vybrousit.
- LeonardoCA
- Člen | 296
Kdyz pouziju button bar jako radioboxy z twitter bootstrapu a misto buttonu mam ajaxove odkazy, takto nejak:
<div class="btn-group " data-toggle="buttons-radio">
<a class="btn btn-primary button-small ajax" href="{link ...">odkaz1</a>
<a class="btn btn-info button-small ajax" href="{link ...}">odkaz2</a>
</div>
prestane fungovat prepinani stavu tlacitek, bez ajaxu funguje jak ma
@vojtech.dobes: nebudes vedet nahodou zhlavy kde je problem? je to bug nebo delam zkousim neco co bych nemel? :-)
„buttony“ obsluhuje https://github.com/…ap-button.js
Editoval LeonardoCA (30. 8. 2012 13:56)
- Vojtěch Dobeš
- Gold Partner | 1316
Otevřel jsem diskusi ohledně využití History API, ocením jakékoliv postřehy: https://github.com/…js/issues/22
- h4kuna
- Backer | 740
Zdravím snažím se to rozchodit a nedaří se mi.
- nalikovaný jQuery
- plugin, $.nette.init() proběhne
- formulář (GET/POST) má třídu ajax, zkoušel jsem i inputu submit dat třídu ajax
A ajax se neprovede stále mi to odesílá klasickým dotazem. Žádný chyby se negenerují.
Tady na tomto řádku se i správně formulář podle selektoru (form.ajax) vybere.
Co jsem přehlédl?
Editoval h4kuna (6. 9. 2012 11:21)
- h4kuna
- Backer | 740
Je to čistý formulář, takže o ničem nevím, jen si s tím hraju takže nic složitýho. A případně jak se dá nejefektivněji dohledat zda má formulář na sobě nějakou událost? Ve firebugu jsem nic takovýho nenašel.
EDIT
Vyřešeno po chatu, děkuji vojtech.dobes
Editoval h4kuna (6. 9. 2012 13:54)
- Vojtěch Dobeš
- Gold Partner | 1316
Průšvih byl ve využití onchange
atributu – odeslání
formuláře je třeba volat na jQuery objektu.
<input onchange="submit()">
Tohle totiž vždy formulář odešle neajaxově.
- duskohu
- Člen | 778
Zdravím dalo by sa pomocou tohto doplnku urobiť volanie handle ajaxovo? Napríklad keď mám riadok tabulky tr a po odkliknuti mi zavolá handle. Mám takíto príklad kde volám handle neajaxovo, alebo ak mám parameter ajax tak by som to chcel volať ajaxovo.
<tr click-handle='{l}"url":"{link setTaskId! $task->id}", "type":"ajax"{r}'>
$('tr[click-handle]').click(function(e){
e.preventDefault();
var obj = jQuery.parseJSON($(this).attr('click-handle'));
if(obj.type=='ajax'){
// ajaxové volanie handle
}else{
window.location = obj.url;
}
});
Editoval duskohu (11. 9. 2012 22:09)
- Vojtěch Dobeš
- Gold Partner | 1316
Yeah, very simple:
$.nette.ext({
load: function () {
$('tr[data-click-handle]').click(function(e){
e.preventDefault();
var obj = $(this).data('clickHandle');
if (obj.type == 'ajax') {
$.nette.ajax({
url: obj.url
});
// ajaxové volanie handle
} else {
window.location = obj.url;
}
});
}
});
Na podobné úlohy je vhodné použít datové atributy – rozdíl je jen
v prefixu data-
, ale je to pak dle specifikace HTML5 a například
jQuery pro to má zabudovanou podporu.
Ajaxový trik je v metodě $.nette.ajax()
.
Toto řešení se chová jako při volání live()
, takže není
třeba se trápit s novými odkazy vrácenými ve snippetu.
- Vojtěch Dobeš
- Gold Partner | 1316
Celou extenzi history
přepracovávám, protože aktuální
verze je spíš takový nástřel (například neřeší signály).
Každopádně pro odkaz lze vypnout, stačí přidat odkazu atribut:
<a n:href="something" data-ajax-off="history">Something</a>
- peter.z
- Člen | 37
Zdravim,
chcel by som sa spytat na confirm dialog pred ajaxovou poziadavkou, napr. pri odstranovani udajov z DB. Chcel by som to mat riesene cez data-confirm, ale vobec neviem, kam mam dat to overenie, ci ma element tento atribut. Funkcnost by mala byt jednoducha – ak element obsahuje data-confirm, vyhodi sa confirm dialog, ak potvrdi, vykona sa ajaxova poziadavka, ak zamietne, nic sa nestane.
Dakujem :)
- Vojtěch Dobeš
- Gold Partner | 1316
Well, you can do that with plain Javascript:
$.nette.ext({
load: function () {
$('.ajax').click(function () {
var $this = $(this);
var question = $this.data('confirm');
if (question) {
return confirm(question);
}
});
}
});
Doufám, že se to nebude bít s ajaxifikací. Bylo by lepší použít
událost start
, ale musím ji opravit – nyní nedostává
argument, ve kterém je ajaxifikovaný element.
- Vojtěch Dobeš
- Gold Partner | 1316
V tom případě si napiš tohle rozšíření:
$.nette.ext({
start: function (xhr, settings) {
var question = settings.nette.el.data('confirm');
if (question) {
return confirm(question);
}
}
});
A oprav si řádek 191 v nette.ajax.js
z
}, xhr);
na
}, xhr, settings);
Tuhle úpravu co nejdřív dodám do masteru.
Editoval vojtech.dobes (12. 9. 2012 22:13)
- Vojtěch Dobeš
- Gold Partner | 1316
Hm, vyzkoušel bych to trošku „prolog
ovat“, co je v
settings.nette.el
, co je v question
…
- Kaspis
- Člen | 13
Zdravím :) dneska jsem chtěl nahodit confirm dialog a narazil jsem, presně
jako peter.z . Nicméně objevil jsem, že pokud rozšíření není anonymní,
tak to funguje, dialog vyskočí, pokud dáte „ano“, request se provede a
vše ok, pokud „ne“ zmizí dialog, ale js vyhodí chybu: TypeError:
$.ajax($.extend({beforeSend: function (xhr) {return inner.fire({name:
„start“, off: settings.off || {}}, xhr, settings);}}, settings)).done is not
a function
}, settings)).done(function (payload) { …
js je pro mě pořád dost magický … šlo by s tím něco udělat? Předem
moc díky
ještě to rozšíření:
<script>
$.nette.ext('confirm', {
start: function (xhr, settings) {
var question = settings.nette.el.data('confirm');
if (question) {
return confirm(question);
}
}
});
</script>
- LeonardoCA
- Člen | 296
@vojtech.dobes – Možná by to chtělo přidat ještě událost „cancel“.
Udělal jsem drobný addon pro ladění ajaxu. Nebavilo mne otevírat firebug, když chci jen vědět, které snippety jsou v payload. Detekuje produkční režim tak, že pokud nenajde „#nette-debug“ tak se neaktivuje, ale v produkčním režimu by se nejlépe neměl vůbec vkládat do stránky.
Stačí přidat tento js
Panel se zobrazí při prvním Ajax requestu. Vytvoří lištu na levé straně a posune body doprava o 300px, není to ideální, ale mi to tak vyhovuje.
A potřebuje už mít přidán do „start“ parametr „settings“!
Editoval LeonardoCA (14. 9. 2012 14:56)
- Vojtěch Dobeš
- Gold Partner | 1316
@peter.z, @Kaspis: anonymní
extenze byly bugnuté, opraveno. Stejně tak byla chyba u události
start
(evidentně ji ještě nikdo předtím nepoužil :) ), též
opraveno.
A v masteru se již této události předává jako druhý argument objekt
settings
.