
Following on from my post of a couple of weeks ago, I'm rendering a *large* (~150 editable fields) form for a CRUD application and am wanting client-side processing of incremental updates to said form, since round-tripping adds considerable delay to the user experience. I'm sending the form as a table with appropriate athena:handler defined. On a mouse click, the athena:handler replaces the <span> containing the text with an <input type="text" /> or other control as appropriate. What I'm then trying to do is hook up an event handler to onBlur (or whatever) to compare the old and new values, set a dirty flag and enable the "save" button. I couldn't find a way to do this with the Divmod.NotQuiteMochiKit packages (indeed, they seem wedded to innerHTML), so I whacked MochiKit in the page header and tried this: class Frag(athena.LiveFragment): jsModule = u"db.Record" docFactory = loaders.stan( T.table(render=T.directive('sequence'), data=T.directive('sql'))[ T.tr(pattern='item', render=T.directive('mapping'))[ T.td[T.slot(name='fieldName')], T.td[ athena.handler(event='onclick', handler='editField'), T.span[T.slot(name='fieldValue')], ], # end of td ], # end of tr ]) ...and the jsmodule has: db.Record.methods( function editField(self, node, event) { if (node.inEdit) return false; /* make the textbox */ var tb = INPUT({'type': 'text', 'value': scrapeText(node)}); /* swap the span for a textbox */ replaceChildNodes( getElementsByTagAndClassName('span', null, node)[0], tb ); /* THIS FAILS */ connect(tb, 'onblur', self.saveField); return false; }); The nevow debug console thing logs "self has no properties" - what is self in this context of a widget method/event call? Why does it have no properties? Absent a more complete set of DOM and event handling functions, and given whatever the (to me) unfathomable incompatibility with Mochi and the Divmod runtime is, how does one attach event handlers to client-side created DOM nodes? Are the events normalised cross-browser as they are in Mochi? There's an "xbevent" function in one of the examples that indicates not? Suggestions welcome. Sorry for abruptness of mail, workrave is nagging me to stop typing...

On Sun, 01 Oct 2006 00:14:14 +0100, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
[snip]
db.Record.methods( function editField(self, node, event) { if (node.inEdit) return false; /* make the textbox */ var tb = INPUT({'type': 'text', 'value': scrapeText(node)}); /* swap the span for a textbox */ replaceChildNodes( getElementsByTagAndClassName('span', null, node)[0], tb );
/* THIS FAILS */ connect(tb, 'onblur', self.saveField);
return false; });
The nevow debug console thing logs "self has no properties" - what is self in this context of a widget method/event call? Why does it have no properties?
Athena doesn't automatically bind methods to instances, unfortunately. This means when the onblur handler is invoked, saveField will be invoked without any reference to the widget it came from. Athena will let this happen, but self won't be bound properly. Try connecting this instead: function() { self.saveField(); } Jean-Paul

On Sat, 30 Sep 2006 19:36:28 -0400, Jean-Paul Calderone <exarkun@divmod.com> wrote:
On Sun, 01 Oct 2006 00:14:14 +0100, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
Athena doesn't automatically bind methods to instances, unfortunately. This means when the onblur handler is invoked, saveField will be invoked without any reference to the widget it came from. Athena will let this happen, but self won't be bound properly. Try connecting this instead:
function() { self.saveField(); }
Ignore my theories, this is the correct explanation.

Jean-Paul Calderone wrote:
The nevow debug console thing logs "self has no properties" - what is self in this context of a widget method/event call? Why does it have no properties?
Actually, this seems to be a MochiKit bug - the fact it was appearing in the divmod/nevow debug thing was confusing me. Mochikit's connect() seems to be referencing a non-existent "self" somewhere. Grumble. Sorry for the noise

Phil Mayers wrote:
Jean-Paul Calderone wrote:
The nevow debug console thing logs "self has no properties" - what is self in this context of a widget method/event call? Why does it have no properties?
Actually, this seems to be a MochiKit bug - the fact it was appearing in the divmod/nevow debug thing was confusing me. Mochikit's connect() seems to be referencing a non-existent "self" somewhere. Grumble.
Sorry for the noise
Ha. Actually it's not. I'm using Nevow 0.9 which still has Mochikit in it so I'm failing to load my local version, and the version in Nevow 0.9 does *not* have the MochiKit.Signal module. Sigh...

On Sun, 01 Oct 2006 13:13:07 +0100, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
Phil Mayers wrote:
Jean-Paul Calderone wrote:
The nevow debug console thing logs "self has no properties" - what is self in this context of a widget method/event call? Why does it have no properties?
Ha. Actually it's not. I'm using Nevow 0.9 which still has Mochikit in it so I'm failing to load my local version, and the version in Nevow 0.9 does *not* have the MochiKit.Signal module. Sigh...
OK, so part of what I said was right, except only by accident. You want a (more recent) version of Nevow without MochiKit. I realize that you're probably not running off trunk@HEAD for good reason - things tend to destabilize occasionally, especially if you're not following the daily hustle and bustle on #divmod. Our internal testing process involves creating "releases", though. Some time in the future, when we have more resources, they'll be properly kept up to date on the website, announced, QA'd and so on. For the time being though, you can split the difference between trunk@HEAD and "latest released, publicised tarball" by using these interim releases, which you can see here: http://divmod.org:81/svn/Divmod/tags/releases/ Nevow 0.9.9, for example, doesn't seem to contain MochiKit.

glyph@divmod.com wrote:
Our internal testing process involves creating "releases", though. Some time in the future, when we have more resources, they'll be properly kept up to date on the website, announced, QA'd and so on. For the time being though, you can split the difference between trunk@HEAD and "latest released, publicised tarball" by using these interim releases, which you can see here:
http://divmod.org:81/svn/Divmod/tags/releases/
Nevow 0.9.9, for example, doesn't seem to contain MochiKit.
Ah ha. That's useful, thanks. I should just add by the way that I think the Divmod team are very kind supporting the (non-paying!) community of users, especially given your relatively small size and need to earn a living at the same time. I'll have to see what I can do about getting my employers to join the fan club.

On Sun, 01 Oct 2006 00:14:14 +0100, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
What I'm then trying to do is hook up an event handler to onBlur (or whatever) to compare the old and new values, set a dirty flag and enable the "save" button.
I couldn't find a way to do this with the Divmod.NotQuiteMochiKit packages
I assume you're talking about Divmod.Runtime?
(indeed, they seem wedded to innerHTML), so I whacked MochiKit in the page header and tried this:
Some parts of the Divmod infrastructure still use MochiKit. We're trying to remove this dependency in the future to allow Nevow users to use whatever version of MochiKit they like, but at least for the time being you can get to the version of MochiKit distributed with Nevow by putting an "//import MochiKit.Whatever" statement at the top of your JS module.
class Frag(athena.LiveFragment): jsModule = u"db.Record" docFactory = loaders.stan( T.table(render=T.directive('sequence'), data=T.directive('sql'))[ T.tr(pattern='item', render=T.directive('mapping'))[ T.td[T.slot(name='fieldName')], T.td[ athena.handler(event='onclick', handler='editField'), T.span[T.slot(name='fieldValue')], ], # end of td ], # end of tr ])
I don't see a render=T.directive("liveFragment") anywhere, so this fragment is going to be complete garbage by the time it reaches the client. There really ought to be a warning or something but it's a hard thing for the infrastructure to inspect.
...and the jsmodule has:
db.Record.methods( function editField(self, node, event) { if (node.inEdit) return false; /* make the textbox */ var tb = INPUT({'type': 'text', 'value': scrapeText(node)}); /* swap the span for a textbox */ replaceChildNodes( getElementsByTagAndClassName('span', null, node)[0], tb );
/* THIS FAILS */ connect(tb, 'onblur', self.saveField);
return false; });
The nevow debug console thing logs "self has no properties" - what is self in this context of a widget method/event call?
It *should* be an instance of db.Record in this case.
Why does it have no properties?
I am not completely sure but I have a few theories: * You didn't include the appropriate render directive to set up the widget. * You didn't set 'self.saveField = XXX' anywhere. * You stomped on the version of MochiKit that Divmod was using internally.
Absent a more complete set of DOM and event handling functions, and given whatever the (to me) unfathomable incompatibility with Mochi and the Divmod runtime is, how does one attach event handlers to client-side created DOM nodes? Are the events normalised cross-browser as they are in Mochi? There's an "xbevent" function in one of the examples that indicates not?
The major "incompatibility" is that it's a huge cost to pay on top of the already too-large Divmod runtime. We're trying to reduce the dependency so that we don't require all users to pay that cost. However, far from being incompatible, Nevow *includes* a lightly modified version of MochiKit, and will probably continue to so that you can use it as a "divmod module" in Athena applications, even after the Athena core no longer uses it.

On Sat, 30 Sep 2006 19:38:54 -0400, glyph@divmod.com wrote:
On Sun, 01 Oct 2006 00:14:14 +0100, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
I assume you're talking about Divmod.Runtime?
(indeed, they seem wedded to innerHTML), so I whacked MochiKit in the page header and tried this:
Some parts of the Divmod infrastructure still use MochiKit.
Ahem. I am confused. This dependency is in *Mantissa*, not Nevow, and the version of MochiKit there will only conflict with you if you're using chunks of Mantissa which import it. So technically this sentence here is correct, "some parts of the Divmod infrastructure" do still use MochiKit. But there were several others which were not.

On Sun, 01 Oct 2006 11:27:43 +0100, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
glyph@divmod.com wrote:
I don't see a render=T.directive("liveFragment") anywhere, so this
That was a typo (I was removing style-related stan as I went) - the render directive is there.
A pity - that was the only correct observation I made :).

glyph@divmod.com wrote:
Some parts of the Divmod infrastructure still use MochiKit. We're trying to remove this dependency in the future to allow Nevow users to use whatever version of MochiKit they like, but at least for the time
Would you expect that to work completely? How will the two kits interact with regards things like e.g. addLoadEvent, addToCallStack, connct() and so forth? Also, I'm not an expert, but don't many browsers limit you to 2 HTTP requests outstanding to a given server at once, meaning it's likely you'd run into problems running e.g. the Mochikit xmlhttp and the Athena one? What I'm trying to get a feel for with the Athena stuff is what parts of the javascript runtime, if any, are supposed to be used, and which parts are private and just for implementing callRemote and friends.

On Sun, 01 Oct 2006 13:13:11 +0100, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
glyph@divmod.com wrote:
Some parts of the Divmod infrastructure still use MochiKit. We're trying to remove this dependency in the future to allow Nevow users to use whatever version of MochiKit they like, but at least for the time
Would you expect that to work completely? How will the two kits interact with regards things like e.g. addLoadEvent, addToCallStack, connct() and so forth?
It depends on your application. Athena doesn't internally generate much DOM client side, or attempt to handle your events. If you do that with MochiKit it shouldn't conflict.
Also, I'm not an expert, but don't many browsers limit you to 2 HTTP requests outstanding to a given server at once, meaning it's likely you'd run into problems running e.g. the Mochikit xmlhttp and the Athena one?
Yep. This isn't a library problem though: if you're using Athena, you _must not_ use any other XHR APIs in your page. Athena goes to a good deal of trouble to allow you to have more than 2 outstanding athena requests when the browser allows you only 2 HTTP requests. It needs both of those requests though, one to wait for incoming notifications and one to make outgoing requests.
What I'm trying to get a feel for with the Athena stuff is what parts of the javascript runtime, if any, are supposed to be used, and which parts are private and just for implementing callRemote and friends.
I hope someone more knowledgeable about the intent of those APIs will answer. Technically speaking the rule is that the APIs are public unless they start with "_", but "public" doesn't necessarily mean "supported in perpetuity". Please feel free to ask about specific things though; hopefully someone more knowledgeable about the API's intent than I will answer.

glyph@divmod.com wrote:
Also, I'm not an expert, but don't many browsers limit you to 2 HTTP requests outstanding to a given server at once, meaning it's likely you'd run into problems running e.g. the Mochikit xmlhttp and the Athena one?
Yep. This isn't a library problem though: if you're using Athena, you _must not_ use any other XHR APIs in your page. Athena goes to a good deal of trouble to allow you to have more than 2 outstanding athena requests when the browser allows you only 2 HTTP requests. It needs both of those requests though, one to wait for incoming notifications and one to make outgoing requests.
Ah. That's an issue. This rules out safely calling other non-Nevow RPCs (which might be glued into the same server & URL space by reverse proxying, and in fact are in my case). Obviously I can write (tedious) code to execute those RPCs on the server, but scalability aside it also breaks single sign-on. (Note: I'm not saying Nevow or Athena are doing the wrong thing here - as you correctly say, this is a browser limitation). I think this can be solved using IFRAMEs to make the RPCs, but at the expense of another server name (and IP if using SSL in fact). Will have to look into that.
What I'm trying to get a feel for with the Athena stuff is what parts of the javascript runtime, if any, are supposed to be used, and which parts are private and just for implementing callRemote and friends.
I hope someone more knowledgeable about the intent of those APIs will answer. Technically speaking the rule is that the APIs are public unless they start with "_", but "public" doesn't necessarily mean "supported in perpetuity".
Please feel free to ask about specific things though; hopefully someone more knowledgeable about the API's intent than I will answer.
Well, the specific thing I was thinking of was the JSON-RPC but knowing that Athena consumes both available XHR requests makes the point moot. Interestingly, it seems we've reached a situation analogous with the desire for a single event loop in the python standard library, only this time in the JavaScript VM ;o) For the info of anyone reading, I did eventually get all this working, and am using MochiKit for the client-side DOM stuff but calling athena widget callRemote channels - cool stuff like (untested copy&paste): function saveEditable(evt) { var tb = event.src(); var span = tb.parentNode; var widget = Nevow.Athena.Widget.get(span); /* only if it has changed... */ if (tb.value!=tb.orig) { /* mark it dirty */ addElementClass(span, 'dirty'); /* push update to server */ var d = widget.callRemote('update', span.id, tb.value); /* wait for ok/fail */ d.addCallbacks( /* ok, remove dirty mark */ function(v) { removeElementClass(span, 'dirty'); }, /* fail, display error for 5 seconds, function(e) { var v = scrapeText(span); replaceChildNodes(span, e); callLater(5, replaceChildNodes, span, v); }); } /* convert the span back to a plain text holder */ replaceChildNodes(span, tb.value); removeElementClass(span, 'editing'); } function makeEditable(evt) { var span = evt.src(); if (hasElementClass(span, 'editing')) return false; /* get the contents of the span */ var text = scrapeText(span); /* put it into a text field... */ var tb = INPUT({'type': 'text', 'value': text}); tb['orig'] = text; /* ...inside the span */ replaceChildNodes(span, tb); tb.focus(); connect(tb, 'onblur', saveEditable); addElementClass(span, 'editing'); } function setupEditFields(node) { forEach( getElementsByTagAndClassName('span', 'editable', node), function(n) { connect(n, 'onclick', makeEditable); }); } myApp.myWidget = Nevow.Athena.Widget.subclass('myApp.myWidget'); myApp.myWidget.methods( function __init__(self, node) { myApp.myWidget.upcall(self, '__init__', node); setupEditFields(node); });

On Sun, 01 Oct 2006 23:57:01 +0100, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
[snip]
For the info of anyone reading, I did eventually get all this working, and am using MochiKit for the client-side DOM stuff but calling athena widget callRemote channels - cool stuff like (untested copy&paste):
function saveEditable(evt) { [snip] }
function makeEditable(evt) { [snip] }
function setupEditFields(node) { [snip] }
myApp.myWidget = Nevow.Athena.Widget.subclass('myApp.myWidget'); myApp.myWidget.methods( function __init__(self, node) { myApp.myWidget.upcall(self, '__init__', node); setupEditFields(node); });
I wonder why you chose to make those three functions free instead of methods of your widget class? Jean-Paul

Jean-Paul Calderone wrote:
I wonder why you chose to make those three functions free instead of methods of your widget class?
Conceptually, one might have multiple different widgets wanting the editable span functionality which cannot easily share a base class, and also since I'm told Athena does very little client-side DOM work, to separate out the athena-specific code from the client-side DOM code. But it would have worked just as well on the widget. Third reason - VIMs syntax highlighting fails for encapsulated functions but works for bare ones :o)

Really? The syntax highlighting works for me using Vim 7. Now if I could only find syntax folding for Javascript. C On 10/2/06, Phil Mayers <p.mayers@imperial.ac.uk> wrote:
Jean-Paul Calderone wrote:
I wonder why you chose to make those three functions free instead of methods of your widget class?
Conceptually, one might have multiple different widgets wanting the editable span functionality which cannot easily share a base class, and also since I'm told Athena does very little client-side DOM work, to separate out the athena-specific code from the client-side DOM code.
But it would have worked just as well on the widget.
Third reason - VIMs syntax highlighting fails for encapsulated functions but works for bare ones :o)
_______________________________________________ Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
participants (4)
-
Cory Dodt
-
glyph@divmod.com
-
Jean-Paul Calderone
-
Phil Mayers