[Seaside] Repainting during AJAX callbacks

C. David Shaffer cdshaffer at acm.org
Fri Sep 3 18:00:41 UTC 2010


 Before I get too far let me say that I don't know, yet, whether this is
an unnecessary hack or a worthwhile framework.  I'm using it now and
like it but I'd be happy to replace it with something better is people
can tell me what that better thing is :-).

Here's the problem: During an AJAX callback (request, update or script)
sometimes a component unrelated to the one making the callback needs to
be repainted.  Normally this happens as the result of an announcement. 
While announcements serve to decouple the components they also prevent
the source of the announcement from knowing to cause this other
component to paint.

Basically this motivates using Comet but I don't like Comet and find
that I don't need Comet's level of "server push" for any of my
applications.  What I need is simply the way tack a little extra
javascript on the end of AJAX calls...

My solution: Repaintable in the Cincom Public Store Repository.  (This
package uses JQuery.)  It is a very simple framework so easily abandoned
if it is the wrong thing to do :-)  Here's how it works:  Components
subclass RPRepaintable (or you merge RPRepaintable's methods into your
component hierarchy if that isn't possible).  You must also use
RPSession or a subclass as your session class for your application. 
When a component wants to append javascript onto the end of the current
response to an AJAX request it:

    self repaint: 'some-jquery-selector' using: [:h | "h is a canvas"]

The block with be invoked with a canvas.  Paint on it was you normally
would.  Its contents will be loaded by jQuery into the element(s)
indentified by 'some-jquery-selector' (normally an element id).

To simplify things, if the component wants to be able to completely
repaint itself it can implement the following trio of methods:

renderContentOn: html
    html div
        id: self contentId;
        with: [self paintOn: html]

contentId
    ^'some-unique-id'

paintOn: html

    html text: 'Put your normal renderContentOn: stuff here'


Then, such a component just has to send itself #repaint to repaint its
contents.  So, suppose you receive an announcement and you want to
repaint yourself.  Just do "self repaint".  Its that simple.  It works
whether the original AJAX request was "script" or "html" type.  Sending
#repaint (#repaint:using) during a full-page repaint (normal HTTP
request) does nothing...your component will be painted during the render
phase anyway so normally this is not a problem.

An example is provided in the Public Store Repository
(Repaintable-example).  Some notes:

1) All AJAX requests must either be dataType 'script' or 'html'.  This
means that cases when you don't render anything like:

html div
    onClick: ((html jQuery: #foo) ajax callback: [self foo])

will have to modified to

html div
    onClick: ((html jQuery: #foo) ajax dataType: 'script'; callback:
[self foo])

I think that this this is the result of a bug in Repaintable but I can't
tell.  In practice 99% of my AJAX requests are either html: or script:
requests anyway.  In those cases you don't need to send dataType:.

2) If you are using AJAX for purposes other than sending back HTML or
javascript you might end up with cruft being appended to your responses
:-) as components try to repaint themselves.  This isn't a problem for
me but it might be for those of you using JSON to transfer data etc. 
This could be fixed if it becomes a problem for anyone.

So, garbage or pure genius (I know the answer already as it took me
three attempts to spell genius)?

David



More information about the seaside mailing list