Adding Athena JS handlers in render methods?

Hi all. I hit something earlier today that has me stumped. It might be a case of programming blindness, but on the other hand I might just be trying to do something that isn't supported. Up until now, I've been adding Athena event handlers directly in the docFactory's stan. However, I needed to add something with an Athena event handler that needs data from an asynchronous external source, so I added data and render methods. These normally Just Work, in the case of "vanilla" XHTML, but the Athena handlers don't seem to be processed by _rewriteEventHandlerToAttribute(). In other words, instead of a proper event handler being inserted into the XHTML, like so: <a href="#" onclick="return Nevow.Athena.Widget.handleEvent(this, "onclick", "onclick");">Click Me 0</a> a tag like this appears: <a href="#"><athena:handler handler="onclick" event="onclick"></athena:handler>Click Me 1</a> Here's some sample code to illustrate this. It's not a complete example in that there's no JS code to be invoked when you click on the first link, but it's runnable enough that you can load the page and view the page source. The first link, "Click Me 0", has correct event handlers inserted. The second and third links ("Click Me 1" and "Click Me 2") both have incorrect event handlers inserted. One of them simply returns a piece of stan, the other also makes a supposedly asynchronous call. Am I trying to do something unsupported, or is this a bug? Thanks, Ricky == from twisted.internet import defer from nevow import athena from nevow import loaders from nevow import flat from nevow import tags as T from nevow.page import renderer _js = athena.handler ( event = 'onclick', handler = 'onclick' ) class MyElement ( athena.LiveElement ): docFactory = loaders.stan ( T.div ( render = T.directive ( 'liveElement' ) ) [ T.a ( href = '#' ) [ _js, "Click Me 0" ], T.br, T.div ( render = T.directive ( 'someStan1' ) ), T.br, T.div ( render = T.directive ( 'someStan2' ) ), ] ) def someStan1 ( self, context, name ): return T.a ( href = '#' ) [ _js, "Click Me 1" ] renderer ( someStan1 ) def someStan2 ( self, context, name ): def _cb ( r ): return T.a ( href = '#' ) [ _js, r ] d = self._getData() d.addCallback ( _cb ) return d renderer ( someStan2 ) def _getData ( self ): return defer.succeed ( "Click Me 2") class MyPage ( athena.LivePage ): docFactory = loaders.stan ( T.html [ T.head ( render = T.directive ( "liveglue" ) ), T.body [ T.div ( render = T.directive ( "myelement" ) ) ], ] ) def render_myelement ( self, request, tag ): e = MyElement() e.setFragmentParent ( self ) return e ###################################################################### from twisted.application import service, internet from nevow import appserver port = 7060 application = service.Application ( "example" ) res = MyPage() site = appserver.NevowSite ( res ) webService = internet.TCPServer ( port, site ) webService.setServiceParent ( application )

On Tue, 24 Jul 2007 13:42:36 +0300, kgi <iacovou@gmail.com> wrote:
Hi all.
I hit something earlier today that has me stumped. It might be a case of programming blindness, but on the other hand I might just be trying to do something that isn't supported.
Up until now, I've been adding Athena event handlers directly in the docFactory's stan. However, I needed to add something with an Athena event handler that needs data from an asynchronous external source, so I added data and render methods.
[snip - event handlers in stan returned by render/data methods don't work]
Am I trying to do something unsupported, or is this a bug?
It's unsupported. What you can do instead is to put the event handlers into the template (stan or xhtml) as patterns. This will result in them being rewritten properly. Your render and data methods will thus be able to load them, already pre-processed, from the docFactory and put them in the page. Jean-Paul

On Monday 30 July 2007 17:25:43 Jean-Paul Calderone wrote:
It's unsupported. What you can do instead is to put the event handlers into the template (stan or xhtml) as patterns. This will result in them being rewritten properly. Your render and data methods will thus be able to load them, already pre-processed, from the docFactory and put them in the page.
Hi Jean-Paul; thanks for your answer; that makes a lot of sense. In the meantime I worked around the problem by inserting the "expanded" javascript (that is, Nevow.Athena.Widget.handleEvent()) into the stan in the special methods. It's hacky but it works - I'll implement your suggestion next time I visit that piece of code. If you're not fed up with Athena-related questions, I do have another one. At the moment I've got some active content in a LiveElement that I update multiple times throughout the lifetime of a page. This updating is done by JS DOM manipulation on parameters that get passed over an Athena call (paying greater client complexity and load in return for lower server load and lower bandwidth). However, the content has to have *something* when the LiveElement is first rendered, so I'm having to implement the same logic twice: once in the stan to provide the initial content, and once in the JS to update the page. I'd like to kill the stan version by arranging to call the LiveElement's update code after the page has rendered; however, I'm unsure where the hook should go. I already have code to allow the LiveElements to request a callback, I just need to know when the Athena link is up and running. Digging about a bit, I can see that rend.Page's self.afterRender gets called after renderHTTP, if it's defined, but this is too early: it gets called when rendering has been done on the *server*, way before the browser even starts requesting the various page elements (JS files, CSS files, etc). That also rules out trying to hook onto LivePage's _becomeLive, because that gets called even before renderHTTP. LivePageFactory's addClient() runs similarly early. I've even considered having a special child resource (a 1x1 transparent image, say), placed towards the bottom of the body, access to whose resource path triggers the process of updating the LiveElements, but that's really the last resort. Is there an approved way to achieve this? Thanks, Ricky

* kgi <iacovou@gmail.com> [2007-08-01 16:07:15 +0300]:
I'd like to kill the stan version by arranging to call the LiveElement's update code after the page has rendered; however, I'm unsure where the hook should go. I already have code to allow the LiveElements to request a callback, I just need to know when the Athena link is up and running.
I think the usual way to handle this is to make a client-to-server callRemote from the widget's nodeInserted method; the server-side method could either return the update in response to that call, or simply perform a server-to-client callRemote to do the update. I believe both of those options are equally efficient in terms of network behaviour. -- mithrandi, i Ainil en-Balandor, a faer Ambar

On Wednesday 01 August 2007 16:43:23 Tristan Seligmann wrote:
I think the usual way to handle this is to make a client-to-server callRemote from the widget's nodeInserted method; the server-side method could either return the update in response to that call, or simply perform a server-to-client callRemote to do the update. I believe both of those options are equally efficient in terms of network behaviour.
*Slaps forehead* Of course, that makes perfect sense! I was grepping and glimpse-ing all the .py files in the hope of finding a hook; it never occurred to me to look in the .js files. Thank you very, very much Tristan, you've saved me from a fate worse than death: I was halfway through implementing my dirty "special resource" hack when your reply came in! Ricky
participants (3)
-
Jean-Paul Calderone
-
kgi
-
Tristan Seligmann