Ako fungujú signály?

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

Viem že Nette podporuje signály, ale nenašiel som zatiaľ nejaký podrobnejší popis toho ako fungujú. Ak tomu správne rozumiem, tak sú to v podstate udalosti prenášené medzi klientom a serverom, áno? Alebo som možno úplne mimo :-) Celkom ma to totiž zaujalo, dalo by sa pomocou nich robiť také triky ako javascript autocompletion (na základe js udalosti onkeydown) apod? Prípadne na čo sú určené? Vďaka.

Panda
Člen | 569
+
0
-

Co jsem já pochopil, tak to jsou skutečně události mezi klientem a serverem předávané v URL (parametr ?do), ať už jde o ‚normální‘ požadavek, nebo přes AJAX.

Presenter během svého životního cyklu, konkrétně ve 3. fázi – SIGNAL HANDLING (předchozí 2 jsou STARTUP (inicializace globálních parametrů (mimo jiné i zpracování parametrů, které se týkají signálů), volání startup a action{Action}), volá metodu processSignal, která si bere na starosti vyřízení signálu – vezme komponentu, která se určila v initParams jako příjemce signálu (pokud není určen příjemce signálu, je to presenter samotný) a pošle jí signál. V podstatě to vypadá tak, že pokud daný objekt implementuje rozhraní ISignalReceiver, tak se zavolá metoda signalReceived se jménem signálu v parametru. Další zpracování je na daném objektu. Objekty, které dědí od PresenterControl (zajímavé jsou hlavně objekty dědící od Control a Presenter) reagují tak, že se snaží zavolat metodu handle{signal} s příslušnými parametry (= vezmou se všechny parametry, které přišly s požadavkem, vezme se definice funkce handle{signal}, k argumentům se podle jména dosadí parametry z URL a pokusí se danou metodu zavolat – např. jako prametr $id se předá hodnota z parametru id v URL, jako $something se předá something z URL…). Pokud metoda neexistuje, metoda signalReceived vyvolá výjimku. Signál tedy může přijímat jakákoliv komponenta, presenter nebo objekt, který implementuje ISignalReceiver. David to prostě navrhl téměř dokonale.

Jak bylo řečeno, signál může přijímat cokoliv, co implementuje ISignalReceiver. Mezi hlavní příjemce signálů budou patřit Presentery a vizuální komponenty dědící od Control (a ty se při přijetí signálu automaticky invalidují, což je důležité pro AJAX). Signál má sloužit jako signál pro objekt, že má něco udělat – anketa si má započítat hlas od uživatele, blok s novinkami se má rozbalit a zobrazit dvakrát tolik novinek, formulář byl odeslán a má zpracovat data, čtvereček ve hře ‚fifteen‘ se má posunout na prázdné místo…

V ‚obyčejné neAjaxové‘ aplikaci budou putovat nejčastěji přes odkazy nebo jinou akci, která vyvolá přechod na nové URL (odeslání formuláře). Jak již bylo zmíněno, signál je ve své podstatě normální požadavek, který má jen parametr navíc, takže se celá stránka načítá znovu a na straně serveru se znovu projde celý životní cyklus aplikace a presenteru, jen si to ve vhodný okamžik odskočí zpracovat signál.

U ‚Ajaxové‘ aplikace je situace kousek jiná, vyslání signálu lze navázat na jakoukoliv JavaScriptovou aplikaci, takže můžeme mít zprávu o každém pohybu myší uživatele (pokud by ovšem síťové spojení a zpracování scriptů na straně serveru bylo dostatečně rychlé). Také je zde důležitý fakt, že komponenta přijímající signál se automaticky invaliduje, takže se posílá klientovi znovu její kód, nezávisle na tom, ať už v něm signál vyvolal nějakou změnu, nebo ne. Někdy se to hodí, jindy však bude toto chování nežádoucí.

URL pro signál vytváříme pomocí metody PresenterComponent::link. Jako parametr $destination předáme řetězec {signal}! a jako $args pole argumentů, které chceme signálu předat. Signál se vždy volá na aktuální view s aktuálními parametry, parametry signálu se jen přidají. Navíc se přidává hned na začátku zmiňovaný parametr ?do, který určuje signál.

Jeho formát je buď {signal}, nebo {signalReceiver}-{signal}. {signalReceiver} je název komponenty v presenteru. Proto nemůže být v názvu komponenty pomlčka – používá se k oddělení názvu komponenty a signálu.

Dobře je použití signálů vidět v příkladu ‚fifteen‘ u Nette.

Snad jsem postihl vše, co jsem postihnout chtěl a snad je to alespoň trochu srozumitelné. A snad jsem problematiku pochopil dobře.

Na závěr postu odpověď na položenou otázku:
Autocompletion by s pomocí signálů šlo udělat naprosto v pohodě, stačí na to udělat vhodnou komponentu/snippet, která/který se bude správně invalidovat (v případě komponenty je invalidace automatická) se správným obsahem a volání Ajaxového požadavku navázat na správnou událost. A slouží třeba právě k vytvoření autocompletion.

fiso
Člen | 32
+
0
-

Panda: Vrelá vďaka za popis!

Je možné spraviť signál mimo aktuálnu view? tzn niečo ako {presenter}:{view}:{signal}!

Rozmýšľam ešte nad tým, kde je použitie signálov správne a kde už nie. IMHO by sa malo jednať výsostne o veci ktoré nemajú nejaký side effect na serveri. Ak som správne rozumel, tak signály sa prenášajú iba cez GET požiadavky, ktoré by nemali mať nikdy potenciálne nebezpečné vedľajšie efekty (pozri 9.1.1 Safe Methods, HTTP 1.1, RFC 2616) Tzn. signály by sa nemali používať na nič iné ako na zobrazovanie. Mazanie (niečo ako http://example.com/?do=delete&item=1) by sa nikdy nemalo robiť cez GET, to isté by malo platiť aj pre rôzne ankety a podobne, lebo sa cez to dá spraviť taký CSRF že sa človek z toho môže po… A ťahať autorizačné tokeny do GET nie je nič moc, takže takéto veci treba riešiť cez POST.

Dobře je použití signálů vidět v příkladu ‚fifteen‘ u Nette.

Toto by som robil celé v javascripte. Snáď lepší príklad by bolo to autocompletion.

Osobne si neviem moc dobre predstaviť použitie signálov inde ako na udalosti vyvolané v javascripte. Ak máte nejaký príklad, kľudne sem s ním.

Panda
Člen | 569
+
0
-

fiso napsal(a):

Panda: Vrelá vďaka za popis!

Je možné spraviť signál mimo aktuálnu view? tzn niečo ako {presenter}:{view}:{signal}!

Není, alespoň v současném Nette to nejde. David to někde psal, nevím, jestli plánuje dodělat podporu pro signály jinam, ale spíš ne.

Rozmýšľam ešte nad tým, kde je použitie signálov správne a kde už nie. IMHO by sa malo jednať výsostne o veci ktoré nemajú nejaký side effect na serveri. Ak som správne rozumel, tak signály sa prenášajú iba cez GET požiadavky, ktoré by nemali mať nikdy potenciálne nebezpečné vedľajšie efekty (pozri 9.1.1 Safe Methods, HTTP 1.1, RFC 2616) Tzn. signály by sa nemali používať na nič iné ako na zobrazovanie. Mazanie (niečo ako http://example.com/?do=delete&item=1) by sa nikdy nemalo robiť cez GET, to isté by malo platiť aj pre rôzne ankety a podobne, lebo sa cez to dá spraviť taký CSRF že sa človek z toho môže po… A ťahať autorizačné tokeny do GET nie je nič moc, takže takéto veci treba riešiť cez POST.

Není pravda, že se posílají jen přes GET požadavky. Ano, základní upozornění na signál je v URL požadavku, ale to neznamená, že není možné použít POST a data samotná poslat právě v něm. Třeba formulář si také posílá signál, že byl odeslán, ale data samotná putují v POST. Nebo AJAX v Nette s knihovnou nette.js: každý požadavek je POST, i když jeho tělo je třeba prázdné a data samotná jsou v URL.

Jinak souhlasím, akce s vedlejšími účinky by se měly vykonávat přes POST, ale někdy nemusí být 50 formulářů na stránce nebo odkaz na další stránku, kde bude formulář umožňující akci s položkou/vybranými položkami, žádoucí a je lepší to nahradit klasickým odkazem. Pokud se to udělá správně, tak CSRF nehrozí. Nic Ti nebrání udělat token jako persistentní parametr presenteru, jeho ověření nandat do metody processSignal a vytvoření nového tokenu a invalidaci starého do saveState. Pak už se Ti autorizační tokeny generují, přidávají do URL, ověřují a invalidují naprosto samy a Ty se o nic nemusíš starat. Tím si také současně zabezpečíš formuláře, protože jejich odeslání probíhá právě přes signál. Řešil jsem třeba takto změny pořadí objektů na stránce – měl jsem tabulku s objekty, které měly být na stránce, a u každého 2 odkazy s šipkami: nahoru a dolů. Když měl uživatel JavaScript, šlo to přes AJAX, jinak přes normální refresh stránky. Zdálo se mi to lepší, než kopa formulářů nebo přesměrovat uživatele na jinou stránku, kde ten formulář bude.

Toto by som robil celé v javascripte. Snáď lepší príklad by bolo to autocompletion.

Ten příklad původně Ajaxový tuším nebyl, to pak přišlo jako demonstrace toho, jak snadno lze z neAjaxové aplikace udělat krásnou dynamickou aplikačku.

Osobne si neviem moc dobre predstaviť použitie signálov inde ako na udalosti vyvolané v javascripte. Ak máte nejaký príklad, kľudne sem s ním.

Třeba tabulka s hlavičkou, kde klikáním na hlavičku budeš moct měnit způsob řazení. Nebo nějaký seznam produktů, kde kliknutím na název produktu rozbalíš další detaily konkrétního produktu… A nebo již zmiňovaný formulář, který si v signálu posílá upozornění, že se odesílá.

vlki
Člen | 218
+
0
-

Jak je to se signalem, ktery posle formular v komponente?
Vytvari to signal jako [komponenta]-[idformulare]-[signal] a pri zpracovani signalu to hleda komponentu [komponenta]-[idformulare], ktera samozrejme neexistuje…

Je to bug, ktery je opraven dale nebo jen spatne pouziti formularu?

Poznamenavam, ze pouzivam revizi 61, kterou posledni jsem rozjel na php 5.2.0.

David Grudl
Nette Core | 8147
+
0
-

Proč ta komponenta samozřejmě neexistuje?

vlki
Člen | 218
+
0
-

Protoze je tam zaregistrovana jako {komponenta}. A globalni komponenta {komponenta}-{idformulare} neexistuje.

Editoval vlki (8. 10. 2008 16:56)

David Grudl
Nette Core | 8147
+
0
-

Nemám sílu zkoumat, jak to fungovalo ve starších revizích, zkus to prosím sám v aktuální.

vlki
Člen | 218
+
0
-

Zkusim a reportnu.
Ptal jsem se hlavne kvuli tomu, jestli jsi s tim nemel uz nejake zkusenosti.

Kazdopadne diky nejen za tu rychlou odezvu.

vlki
Člen | 218
+
0
-

Ehm, stydim se, problem vyresen.

Subkomponenta (AppForm) se v komponente inicializovala az pri vykreslovani komponenty.

Omlouvam se za plany poplach.