Suche
Close this search box.

One day with AJAX

I am now on the way home from the sprint after staying 2 additional days in Oslo. And while I wasn’t
doing much coding at the sprint in favour of doing videos I actually did get a bit into AJAX in Oslo as
Balazs Ree and Raphael Ritz were also staying over night and because Balazs gave us a little introduction
into kukit and azax.

As you might know there has been lots of discussion at the sprint about the way AJAX should be implemented
in Plone and at the beginning it was expected to be more a discussion about whether to use MochiKit or
Prototype. But for some reason this was settled quite fast in favour of Prototype. But apparently that
was not all to be discussed by the two AJAX „camps“ in the community, being Ben Saller with his Bling
framework on the one side and kukit/azax on the other side (basically kukit is the Zope independant part
of azax but for the rest of this article maybe think of both as one thing for simplicity). Here
people like Godefroid Chapelle, Balazs Ree, Florian Schulze and Martin Heidegger are involved.
Actually I did not really get what the discussion was all about but I have been told that it has
been about development policies and which way to take for further implementation. Nearly one week (or
sprint) later this also seems to be settled now and all parties agreed on identifying the pieces
on which collaboration is possible and doing so (in fact Godefroid and Ben have been working on
incorporating the two approaches on the last day). But right now it was agreed that both approaches
are still possible to use and they both share the base framework internally. Going from one approach
to the other might then be quite straightforward (but maybe boring).

So as I stayed with Balazs I had the opportunity to get at least an idea of how azax/kukit is working.
Later I will probably also look into Bling in order to get an idea what actually is different.

The AJAX way

For people not that familiar with AJAX let’s first explain with a little example how it works in general.
For that imagine the title on a page to be editable inline. This means that the user can click the
title, it transforms in place into a string input widget, the user changes the title, clicks Save and
it transforms back into the normal title (now with the new content). Of course this also get’s saved
on the server side.

So how does that work internally? First of all there need to be some events which need to be setup. These
are JavaScript events like onClick etc. which do trigger some action inside the kukit JS libs.
These actions can either be client side only or they can trigger something on the serverside which
in turn returns some data (more on that later).

So setting up events with kukit basically looks like follows:

            <rule selector="#field_title">
                <event name="click">getTitleForm</event>
            </rule>
            <rule selector="#title_save">
                <event name="click">saveTitle</event>
            </rule>

(see kukitportlets/browser/kukitportlets.kukit)

This is the syntax right now but it was agreed at the sprint (and even some work has been done by Martin)
to change this in favour of a CSS style syntax, looking like follows:

            #field_title:click {
                remote: getTitleForm
            }

            #title_save:click {
                remote: saveTitle
            }

(Balazs remarked that the XML syntax might be faster to parse so they think about keeping it but my idea
would actually be to use some sort of compilert to convert CSS to XML or any syntax to XML. You
would even have the possibility to create more than one rule from just one instruction in the original
source file. Surely this would need some discussion.)
If you wanna do stuff right now be aware that the CSS syntax is not yet in place and the XML syntax will
probably not the way for doing things in the future, so you have been warned (just wait a little ;-).

Going into detail we see two rules with define a selector (which will later be an CSS selector) on which
events are defined. These selectors select one or more parts of the DOM tree of the page and (in this case
click-) events will be generated for them (without actually touching the template itself). For each event
an action will be defined which will be triggered when that element is clicked.

This file will be read on page load by the JavaScript part of kukit and it will be loaded dynamically (via
a Zope3 view). All the events and actions are parsed then and registered.

The second rule actually defines a part of the page which is not available yet (#title_save) which results
in simply ignoring that rule at that point in time.

So now the events are in place and the user clicks on the title which has the id field_title. An event will
be triggered and the action getTitleForm will be called. It will in fact be called on the server side.
It resides in a Zope 3 view (see kukitportlets/azaxview.py) and the method looks like that:

            def getTitleForm(self):
                title = decode(self.context.Title(), self.context)
                self.setHtmlAsChild('#edit_title',
                    "<div id='archetypes-fieldname-title'>" \
                    "<input size='30' type='text' name='title' value='%s' />" \
                    "</div><input type='button' value='save' id='title_save' />" \
                    "" % title)
                return self.render()

So what it does is to retrieve the title from the object, wrap it into some form and
return that HTML (later this will be rendered by a template, not hardcoded)
with a command called setHtmlAsChild(). Finally the actually payload
for sending back to the client is rendered. The payload in this case is also XML and consists
of certain commands (like setHtmlAsChild) with parameters (the actual HTML form in this
case). And here is maybe also one detail in which Bling and kukit differ as Bling sends
directly JavaScript commands instead of the more abstract one in kukit. As an example
here is an example payload:

            <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
            <html xmlns="http://www.w3.org/1999/xhtml"
                  xmlns:kukit="http://www.kukit.org/commands/1.0">
                <body>
                        <kukit:command selector="div#demo" name="setHtmlAsChild">
                                <kukit:param name="html">
                                        <h1 xmlns="http://www.w3.org/1999/xhtml">it worked</h1>
                                </kukit:param>
                        </kukit:command>
                        <kukit:command selector="div#demo" name="setHtmlAsChild">
                                <kukit:param name="html">
                                        <h1 xmlns="http://www.w3.org/1999/xhtml">it worked&nbsp;again (test)</h1>
                                </kukit:param>
                        </kukit:command>
                </body>
            </html>

The client javascript parses this XML document and does what is necessary like inserting HTML snippets in the
right places. It will also rebind the events again. And as you can see we do have a title_save id in place now
which means that the second rule of the kukit-file is activated. Here basically the same as before happens: a
click triggers an action and this calls a command on the server side, saveTitle() in this case (again in the view)
which looks like follows:

            def saveTitle(self, title):
                self.context.setTitle(title)
                self.context.reindexObject()
                title = decode(self.context.Title(), self.context)
                self.setHtmlAsChild('#edit_title',
                    "<h1 id='field_title'>%s</h1>" % title)
                return self.render()

So it takes the title inside the request, sets in on the object, reindexes it and replaces the edit_title
div again by the original H1 tag but with the new content now.

Entering the real world

Having this simple example we thought in Oslo about how a more realistic use case might look like and which
problems might arise from that. So here it’s in basic use case format (although it is actually too technical
for being a use case theoretically:

            Goal: Change the title of a page

            primary actor: Editor

            Main success scenario:

                1. Editor clicks the Title

                2. Client shows a "Loading..." message in the title div

                3. Client sends command to server, server returns the input form

                4. Editor changes the title and presses Save

                5. Client shows a "Saving..." message in the title div

                6. Client sends command with parameters to server, server saves the changes

                7. Server returns the new title tag, client shows it (replacing the Saving message).

            Exceptions:

                3a: The page is locked by another editor

                    3a1. The server sends an error command to the client

                    3a2. The client shows the problem in the div with an "Unlock" button

                    3a3. The Editor clicks "Unlock"

                    3a4. The client sends a command to the server requesting the unlock

                    3a5. The server unlocks the page and sends back again the input form.

                    3a6. The client shows the input form, go on with 4.

                6a: The request to the server times out.

                    TBD

                6b: The page has been locked in the meanwhile.

                    TBD

                6c: The new content is not validated

                    TBD

            Remarks

                The additional exceptions need to be defined. In these also more errors might
                happen.

Having a look at this use case it turns out to be a bit more complex in reallife. I am also in favour of putting
the error messages in the actual input divs and not in a central location (as well as the Loading and Saving
messages). IMHO it just makes more sense to display it where it belongs (you might also click on more than
one area at once or they might have different errors).

So all in all setup of such a workflow seems to get rather long in terms of creating events and rules
in the config file. It might also be necessary to carry some state along these actions (that state
should probably be in sync between client and server). Moreover having to type such a mass of rules
will most likely introduce typos. This some macro language might be a nice-to-have thing. This would
also add the advantage that you can edit the lowlevel events after generation if you wish to do so.

Then we will enter the area of concepts. Events for itself don’t say much and having a concept of a widget
or similar things will add a lot in terms of programming usability. You might just have one line
of code to write to setup all the necessary events. It will also make the solution to the next problem easier.

This is the problem of marking these elements in the page which are editable in place.
According to limi nobody yet has done it right (as far as he knows probably) and it might involve
a lot of experimentation to check whether something looks and feels right or not. No option should
be to hover over an element and see if it reacts in some way. Geoff Davis proposed to maybe mark all
editable elements at once while hovering over one. But this again would involve some JavaScript events
inside the kukit file probably adding even more to it. Here a highlevel language for defining sets of
related elements will do good. For creating such a language there are several options:

  • creating the forementioned compiler, which converts a one line widget definition into
    all the events needed and writing the config file.
  • creating a JavaScript plugin for kukit summarizing some of the work to be done and handling
    some actions internally.
  • maybe some combination of both. One could start with defining a widget syntax, which just
    compiles in all the rules and view methods (and probably will reuse AT widgets for now although
    I really would like to have it separate from AT) which can later then replaced by some more
    complex custom action (well, it it’s in the core it wouldn’t be custom but at least more complex
    than just setHtmlAsChild().

My requirements for this would be:

  • The highlevel language needs to be flexible and easy to learn and yet powerful
  • You should still be able to adjust things in the lower level
  • no magic involved (like AT’s ClassGen, etc.) Thus nothing should be done implicitly.

So some work still needs to be done to that regard.

Custom JavaScript plugins for kukit

Speaking of plugins earlier I might show how to define them. We actually did one of these in Oslo in order
to try to replace the title directly on the client side with a form instead of having the server roundtrip
(but as Balazs noted and where he’s right: You want to tell the server about the editing because of locking etc.).
But it serves good as an example nevertheless. So first of all we have to rewrite the kukit config file
and use the more verbatim form of the event definition:

            <rule selector="#field_title">
                <event name="click">
                    <action type="kukitportlets.showField"></action>
                </event>
            </rule>

So what we do here is that we call an action kukitportlets.showField on a click on the
title tag which we need to implement now. For that to work I’ve put a plugin.js into the
browser directory (right now of the kukitportlets Product but of course you should do that
in your own product.

The plugin.js contains the following:

            kukit.eventActionRegistry.register("kukitportlets.showField", function (node, params, eventrule) {
                var value = node.innerHTML;
                var new_value = "<input size='30' type='text' name='title' value='"+value+"' /><input type='button' value='save' id='title_save' />";
                node.innerHTML=new_value;
                },
                new kukit.PythonMethodSignature([], {})
            );

So it registers a new action called kukitportlets.showField with the actions registry of kukit and the
function itself. The function body creates the form and replaces the innerHTML of the node we are in
with the form we just generated using the old value of the node (which is the H1).

The last thing we need to do is to register plugin.js with the Zope3 machinery with configure.zcml to get it loaded:

            <azax:registerEventAction
                name="kukitportlets.showField"
                jsfile="browser/plugin.js"
            />

So then it’s basically running but with the problem that the contents of the H1 tag will get replaced
over and over again as the old event is not removed (as we would need to use something like
outerHTML which is non-existing unfortunately). But as noted we want to tell the server nevertheless
about the edit and thus it mainly serves as example here.

So that’s actually where we left it in Oslo. We also tried to get it working under Plone 2.5/3.0 which had
some problems with interfaces (and maybe still has) but some Zope 3 guru will probably give the right
tip for solving it quite easily.. So some basic work which needs to be done:

  • make kukit use Prototype
  • finish the CSS parser
  • implement error handling on an event basis
  • converge Bling and kukit
  • make it work in Plone2.5/3.0

Building on that we probably would like to have:

  • a concept of AJAX widgets preferably with state
  • having sort of macros for defining complex event setups (like widgets)

But stuff like this needs of course lots of discussion (but hopefully not too much ;-) and so I will look
forward to some cool solutions.

Teile diesen Beitrag