Vložení vlastního makra n:confirm

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

Právě jsem si do Nette přidal v lastní makro, n:confirm a byl jse překvapen jak je to snadné, tak se rád podělím. Někam do BasePresenteru, např. do metody startup přidat řádek:

<?php
	[...]
	LatteMacros::$defaultMacros['@confirm'] = 'data-confirm="<?php echo %:formatString% ?>"';
	[...]
?>

Do stránky připojit JS (vyžaduje JQuery 1.3.x a liveQuery plugin). Inspirováno železničáři https://github.com/…src/rails.js

<script>
$(document).ready(function () {
    $.fn.extend({
        triggerAndReturn: function (name, data) {
            var event = new $.Event(name);
            this.trigger(event, data);
            return event.result !== false;
        }
    });

   $('a[data-confirm], button[data-confirm], input[data-confirm]').live('click', function (e) {
        var el = $(this);
        if (el.triggerAndReturn('confirm')) {
            if (!confirm(el.attr('data-confirm'))) {
                return false;
            }
        }
    });
});
</script>

a v šablonách pak můžete používat jednoduše

<a n:href="this" n:confirm="Opravdu chcete smazat search index googlu?">Smazat</a>

vygeneruje

<a href="index.php" data-confirm="Opravdu chcete smazat search index googlu?">Smazat</a>

Editoval Vyki (20. 11. 2010 19:00)

na1k
Člen | 288
+
0
-

Awesome!

Vždycky jsem si myslel, že je to něco strašně složitého a proto confirmy bastlil různými pochybnými způsoby :-[

Nilp
Člen | 65
+
0
-

Ummm a v čem je lepší psát n:confirm místo data-confirm?

Jan Tvrdík
Nette guru | 2595
+
0
-

Máš vyšší abstrakci, což ti umožňuje věci z ní plynoucí (např. v budoucnu hromadně přejmenovat date-confirm na data-xyz-confirm). Jinak je to úplně na nic (pominu-li o pár znaků kratší zápis).

Vyki
Člen | 388
+
0
-

Nilp napsal(a):

Ummm a v čem je lepší psát n:confirm místo data-confirm?

To je podobná otázka jako proč psát Form::FILLED místo :filled. Jak píše Honza. Když někde něco změníš, tak všude, nemusíš to hledat a přepisovat.

sairon
Člen | 32
+
0
-

Možná hloupý dotaz, ale jak to co nejjednodušeji zkřížit s AJAXem? Protože pokud klasicky zAJAXuju všechny odkazy na signály s třídou ajax, při kliknutí na odkaz se sice objeví potvrzovací dialog, ale provede se i AJAXový request.

Vyki
Člen | 388
+
0
-

Ja to zatím udělal tak, že v tom klasickém handleru ignoruju to co jde z odkazu, kde class = ajax

<script>
   $('a[data-confirm], button[data-confirm], input[data-confirm]').live('click', function (e) {
        var el = $(this);

        if(el.hasClass('ajax')){
            return true;
        }

        if (el.triggerAndReturn('confirm')) {
            if (!confirm(el.attr('data-confirm'))) {
                return false;
            }
        }
    });
</script>

a v tam kde registruju udalost pro class = ajax mám

<script>
$("a[href].ajax").livequery("click",function(e){
    e.preventDefault();

    var el = $(this);

    if(el.attr('data-confirm')){
        if (el.triggerAndReturn('confirm')) {
            if (!confirm(el.attr('data-confirm'))) {
                return false;
            }
        }
    }

    $.get(el.attr("href"));

});
</script>

Nic lepšího jsem zatím neměl čas bastlit.

Edit: A samozřejmě, nesmíš zapomenout v nějakém skriptu před těmito dvěma zaregistrovat to rozšíření JQ o funkci triggerAndReturn.

<script>
    $.fn.extend({
        triggerAndReturn: function (name, data) {
            var event = new $.Event(name);
            this.trigger(event, data);
            return event.result !== false;
        }
    });
</script>

Editoval Vyki (21. 11. 2010 21:07)

sairon
Člen | 32
+
0
-

Aha, přesně to mě napadlo taky, tak jsem doufal, že to půjde udělat nějak elegantněji. Nicméně díky, zrovna se mi to hodí do aktuálního projektu, kde jsem to měl zbastlené ještě o něco hůř :)

Honza Marek
Člen | 1664
+
0
-

Myslim, že když v té confirmovací funkci použijete e.preventDefault() a e.stopImmediatePropagation() místo return false, tak to zafunguje i na zajaxované odkazy.

Vladimír Kocourek
Člen | 8
+
0
-

Pro zajímavost se podělím s možností registrace vlastních maker (a rozšíření Latte), kterou používám já.

Podědil jsem Nette\Templates\LatteMacros do své třídy (Catta\LatteMacros), kde definuju svoje makra obdobným způsobem jako původní třída a v přepsané __construct() přidám svoje makra k původním.

<?php
namespace Catta;

class LatteMacros extends \Nette\Templates\LatteMacros
{
	public static $cattaMacros = array(
		'ifempty' => '<?php if (empty(%%)): ?>',
		'/ifempty' => '<?php endif ?>',

		'currentYear' => '<?php echo Date("Y") ?>',
		// další makra dle své libosti
	);

	public function __construct()
	{
		parent::__construct();

		$this->macros = self::$defaultMacros + self::$cattaMacros;
	}

}
?>

A v BasePresenteru potom v metodě templatePrepareFilters() říkám Latte, aby používal moji třídu namísto své.

<?php
public function templatePrepareFilters($template)
{
	$template->registerFilter($latteFilter = new Nette\Templates\LatteFilter());

	$latteFilter->handler = new Catta\LatteMacros;
}
?>
Vyki
Člen | 388
+
0
-

Honza Marek napsal(a):

Myslim, že když v té confirmovací funkci použijete e.preventDefault() a e.stopImmediatePropagation() místo return false, tak to zafunguje i na zajaxované odkazy.

Díky za tip – vyzkouším.

Vladimír Kocourek napsal(a):
Podědil jsem Nette\Templates\LatteMacros do své třídy (Catta\LatteMacros), kde definuju svoje makra obdobným způsobem jako původní třída a v přepsané __construct() přidám svoje makra k původním.

Není to až příliš komplikované? Rozšířit si to výchozí pole LatteMacros::$defaultMacros se mi zdá o poznání snadnější.

Editoval Vyki (21. 11. 2010 22:05)

Vladimír Kocourek
Člen | 8
+
0
-

Není to až příliš komplikované? Rozšířit si to výchozí pole LatteMacros::$defaultMacros se mi zdá o poznání snadnější.

Mě ten můj způsob přijde čistší. :)

edit: A myslím, že třeba pro https://gist.github.com/347052 od Honzy Marka by to bylo i přehlednější. :)

Editoval Vladimír Kocourek (21. 11. 2010 22:11)

sairon
Člen | 32
+
0
-

Honza Marek napsal(a):

Myslim, že když v té confirmovací funkci použijete e.preventDefault() a e.stopImmediatePropagation() místo return false, tak to zafunguje i na zajaxované odkazy.

Ha, to je ono! e.preventDefault() jsem tam zkoušel dát, ale zapomněl jsem na to, že je tady potřeba ještě ta druhá metoda. Díky, po této úpravě to už funguje tak, jak má.

Honza Marek
Člen | 1664
+
0
-

Vladimír Kocourek napsal(a):

edit: A myslím, že třeba pro https://gist.github.com/347052 od Honzy Marka by to bylo i přehlednější. :)

Jasně, ale je pak víc práce to zamontovat… musíš to mít jak v BasePresenteru, tak i v nějaké BaseControle. Když se to hodí staticky do LatteMacros, stačí zavolat jednu metodu v bootstrapu.

David Grudl
Nette Core | 8218
+
0
-

V Nette bude existovat skript nette.js, který bude mimo jiné řešit potvrzování a bude k tomu využívat atribut data-nette-confirm. Takže pokud chcete být dopředně kompatibilní, používejte tento zápis.

bojovyletoun
Člen | 667
+
0
-

Taky jsem shodou ookolností řešil confirmy. jedna možnost je přes $().cllick(), ale neumí ajax.
Zatímco $().live(click,) umí, ale handler je přivěšen až za odeslání (zkoušeno přes e.stopPropagation, e. preventDefault, return false).

Ale nezkoušel jsem e.stopImmediatepropagation- funguje to?

Takže jsem to vyřešil přes jinou třídu -ne ajax ale třebaa ajaxconfirm a dopsat vlastní hanlder:

$('a.ajaxconfirm').live('click', function(event) {
	event.preventDefault();
	if(!window.confirm("Opravdu smazat?"))return false; // zde je řádek navíc oproti normálce
	if ($.active) return;
	$.post(this.href, $.nette.success);
	$.nette.spinner.css({
	    position: 'absolute',
	    left: event.pageX+20,
	    top: event.pageY+80
	});
Vyki
Člen | 388
+
0
-

sairon napsal(a):
Ha, to je ono! e.preventDefault() jsem tam zkoušel dát, ale zapomněl jsem na to, že je tady potřeba ještě ta druhá metoda. Díky, po této úpravě to už funguje tak, jak má.

Můžu se zeptat, kam konkrétně jsi to e.preventDefault()? Zkoušel jsem to dávat k té confirm fci, potom na další místa, ale stejně se to na pozadí tím ajaxem odešle, když nedám podmínku přímo do fce pro ajax odesílání.

srigi
Nette Blogger | 558
+
0
-

Chalani, riesite v tychto svojich confirmatoroch CSRF? Myslim si, ze je dolezite sa nad tym zamysliet. Pozor, je to nieco ine ako $form->addProtection, lebo na stranke nie je milion malych formularikov, ale kopec <a n:hhref> odkazov.

Staci ak vam niekto podstrci na utocnikovej stranke <img src="http://mojaaplikacia/admin/pages/delete/xyz"> kym ste prihlaseny vo svojejapp a je to v pecku.

David, bude Framework toto niekedy nejako ulahcovat?

Aurielle
Člen | 1281
+
0
-

Tohle by celkem efektivně mohl umět řešit ConfirmationDialog ne?

jasir
Člen | 746
+
0
-

srigi napsal(a):

Chalani, riesite v tychto svojich confirmatoroch CSRF? Myslim si, ze je dolezite sa nad tym zamysliet. Pozor, je to nieco ine ako $form->addProtection, lebo na stranke nie je milion malych formularikov, ale kopec <a n:hhref> odkazov.

Kdysi jsem se pokusil to nějak vymyslet

sairon
Člen | 32
+
0
-

Vyki napsal(a):

sairon napsal(a):
Ha, to je ono! e.preventDefault() jsem tam zkoušel dát, ale zapomněl jsem na to, že je tady potřeba ještě ta druhá metoda. Díky, po této úpravě to už funguje tak, jak má.

Můžu se zeptat, kam konkrétně jsi to e.preventDefault()? Zkoušel jsem to dávat k té confirm fci, potom na další místa, ale stejně se to na pozadí tím ajaxem odešle, když nedám podmínku přímo do fce pro ajax odesílání.

U mě naprosto stačí jenom tahle lehká úprava:

$('a[data-confirm], button[data-confirm], input[data-confirm]').live('click', function (e) {
     var el = $(this);
     if (el.triggerAndReturn('confirm')) {
         if (!confirm(el.attr('data-confirm'))) {
         	e.preventDefault();
         	e.stopImmediatePropagation();
             return false;
         }
     }
 });

Důležitá je právě i metoda stopImmediatePropagation(). Nemůže pak činit problémy mixování .live() a .livequery()? Nechce se mi zkoumat, jak se od sebe liší, já si vždycky vystačil s .live(), i tady registruju funkci pro AJAX odeslání i pro confirm dialog přes .live(‚click‘,fce).

Edit: A teď jsem si všiml ještě jedný čertoviny – nejprve je nutné zaregistrovat event pro potvrzení, pak až funkci, která bude odesílat request. Ono je to vlastně celkem logické :)

Editoval sairon (25. 11. 2010 16:24)

jtousek
Člen | 951
+
0
-

sairon: Nevím přesně jak to funguje, ale když použiješ live tak event bubbling funguje opačným směrem než u livequery. Tedy alespoň podle toho jak se mi to divně chovalo když jsem je mixoval. Právě teď používám všude kde to jde live, aby to neblblo, ale odesílání formulářů mi přes live v 1.4.2 nešlo takže tam mám livequery (aktuální z githubu).