[Seaside] Component tree vs. DOM tree
mlucas-smith at cincom.com
Tue Mar 11 12:56:17 UTC 2008
Andreas Tönne wrote:
> if you unify components and brushes to get a true model of the client side
> (how do you really keep this model in sync with the client side if you use
It's a different conceptual model to the norm, that's for sure. What you
do is think of the web browser as a smart client that is like a
Smalltalk image, except that its UI model is hard coded to this 'DOM'
and 'CSS' stuff but it can run code in its own bizarre language called
Over the last several years we've seen an uptake in peoples willingness
advantage of advances in the web browsers capabilities. Firefox 3b4 for
promote this esoteric platform as a unified smart client.
There are numerous other attempts at smart clients out there too, from
Microsoft and Adobe and various other players too. Mozilla even have
their own attempt at a smart platform beyond the standards with XUL...
but at the end of the day, the industry seems generally uninterested in
non-standards compliant smart clients.. so we have XML and CSS and
This has even been reflected in Mozilla's Prism effort, to provide a
client side launcher for websites to pretend that the web browser isn't
a virtual machine in itself such that individual websites look like
independently running network requiring applications. Good on them I say.
But with this change of mindset comes a change in how you write and
drive your server application code. You have to think of the web browser
as something you command as a widget environment instead of a publishing
environment - that is a radical change to current web development
When you make a <div> element and put it on the client, you should think
of that <div> element as a widget that has a configurable display
comes back to your question of how do you keep the client side in sync
with the server side?
In a good client-server model, the messaging protocol will allow you to
have transparent object references. They generally do this by assigning
each object an id that they can refer to the object by on the remote
side. So the approach becomes pretty simple.. when you make an element
on the server side, you give it an id - the client will honor that id
and you now have a common base of reference.
When you want to update the 'widget' on the client side, you can refer
to it by its id. Often with Ajax, you will have client side code that
will make a specific request to the server and update a specific id on
the client side. The server has no control over what will be updated on
the client side except by virtue of having first emitted the code that
will later do the update. This is a little limited - so one of the first
things we did was to stop emitting xml from the server and instead emit
wants. The second step we did was to make this transparent. Since each
widget on the server side had the same properties as the client side, we
could simply modify ourselves on the server side and create a set of
commands we wanted to perform on the client side to keep it in the same
state as the server.
Anytime a piece of information changes on the client side that the
server should know about, such as an event, we would emit a message back
to the server and let it update itself transparently too. However, when
you start getting more advanced you find that there are several nodes
and events and things that happen on the client side that the server
doesn't want or need to know about.
An example of this is a link widget that will do hover in/out to
simulate css's :hover because IE6 can't do it. This is a slightly
redundant example but it scales up to complex widgets that do tables,
sorting, column reordering etc.
So while you have a consistent view of most of the client on the server
side, you don't know all of what the client is doing - you just know
about the things you made and control.
> then this opens the question:
> - who is maintaining the application state and interaction logic?
It's a combination of the server and the client. The client is the one
who actually does the interaction, but it's the server who makes the
decisions. For example, if you have an autocomplete widget, when they
type in to the input box, the input is sent to the server. The server,
in response, creates the drop down div and fills it with stuff and that
the server may have done other things, like turned you text red by
changing some styling, or updating a clock ticking away in the corner of
the screen.. or updated a details pane to reflect the most likely match
of the autocompleter..
Who knows, the sky is the limit because you can treat these widgets like
you would regular GUI widgets. You hook up to them with events on the
server side and you can manipulate your server side DOM tree in as many
ways as you want between the time a Request comes in and the Response
goes out. All the changes are queued up and replayed in order. We got
sophisticated and even know how to compress that list as we queued up
the commands so that we weren't doing redundant stuff.. this was just an
optimization that wasn't actually required.
> UI and application state are not necessarily identical and you do not want
> to keep your whole state in the DOM tree (this would mean your application
> becomes too much dependent on a particular tree). An application state (in
> VisualWorks it is the ApplicationModel, bad name) is the necessary
> abstraction from a particular presentation. So by joining components and
> brushes, you have to introduce a new concept to replace the now missing
> other half of WAComponent.
It's the same concept, .. though I was disagree that this concept has to
be a separate tree. In fact, there's very little difference between an
ApplicationModel and a CompositePart to use VisualWorks terminology.
Both are reusable - except that you have to jump through more hurdles to
reuse an ApplicationModel than a CompositePart.
But yes, you end up creating a bunch of widgets and you hook up to their
events which were fired to you from the client side. In response, you
tell the widgets and parts and things to do stuff and ... their changes
go back to the client. The 'ApplicationModel' concept is really just a
composite widget subclass, such that it can modify itself.
A common application model type widget was one that contained a
'contents' slot that would be updated based on what the current contents
wanted to do. For example, moving from workflow to workflow was done,
underneath the guys, like so:
self topComponent push: aComponent
A number of things would happen here. For one, our application model
would keep a stack of the components so you could 'pop' again afterwards
when the component did #answer(:). Another thing that would happen is if
that aComponent you pushed ono could represent its current state as a
unique URL, it would also tell the web browser to update the URL using
There's a lot more to that trick though.
The key point though is that a URL change is under the control of the
server... it never happens without explicit coding along the lines of:
window location: '.....'
This also means that in terms of continuations, which allow the user to
hit the back button, you do not need them. The only URLs you will ever
emit are ones that can be reproduced correctly from the URL parameters
and name. Any action you take modifies the page in place, so no URL
change happens there either - which means there is no Back operation
when you're making manipulations. It's an interesting alternate way to
solve the Back button problem that doesn't require continuations. This
was the primary reason we made this technology - because VisualAge
Smalltalk didn't have continuations and we wanted the power of Seaside
on VA :)
One thing I've always admired about Seaside is its ability to serve more
than one conceptual model of how to be a webserver. I'd hate for that to
disappear. When we implemented this stuff we tied ourselves closely to
that communicated to each other. In fact, if the Smalltalk code sent a
back to the server (obviously) using ajax and likewise also get a response.
Another thing we did was embrace the Comet pattern - our framework was
called CometWeb. In hindsight, this was overkill. The idea of the Comet
pattern was that the server could make updates to the client without the
client first needing to do some sort of action. There were only a
handful of places this was useful:
b) Updating screen as results came back from a database query in the
> I think splitting up the responsibilities of WAComponent would not be too
> bad for the clarity of the design. But a more elaborate design of components
> seems not to be too popular :-)
> Where did you see that quote from Michael?
It's on the list somewhere.. it might have been on seaside-dev instead
but I don't think it was. The reason why we didn't want the two trees
was because when you break it down, one of the trees is redundant. If
you need to know about the widget and have events hooked up to it - you
can't throw it away. You also want its id. If you say, fine, that's a
WAComponent.. then you no longer need the brushes, because it will have
unification is a natural refactoring when you go down this path.
The obvious problem here is that you're catouring specifically to the
pattern of treating the web browser as a smart client that you can
converse with in a sane way. This is *not* always true. For example, if
you want to emit json or xml or rss or atom.. there are no callbacks..
there is no need to keep id's or state on the server, it's a one shot
path. In this kind of world, the brush idiom makes more sense.
In the end, we had two separate frameworks - one for emitting tree like
things, such as json and xml.. and our CometWeb framework which would
use it to produce static pages, emails, rss feeds, json/xml dumps, atom
search result feeds and the initial pages that a user gets when they
make their first request on an entry point. In short, the emitter/brush
framework was 'low level' to the CometWeb framework.
I'm quite happy to talk about this more... I hadn't really brought it up
before for a couple of reasons:
a) I'd not want to remake it, dividing the community with yet another
web framework is insane and requires redoing all the fantastic leg work
that has already been done with Seaside to make a name for itself
b) Wizard owned the IP, but they have since died a tragic death and
while I could have talked about it and rewritten it, out of respect that
they might have gone somewhere with the technology, I decided not to
talk about it.
c) I'm totally committed to Seaside, wherever its lead developers and
community want to take it. I really love this technology which is one of
the reasons I work on it at Cincom.
So I guess my perspective is this - if this sounds like the sort of
direction that might be useful for Seaside 2.10 or 3.0, fair enough. I'd
like to see that. If it's not, then fair enough too, I'm not too
worried. Seaside is well ahead of the game wrt to web technology already
and will be for years and years to come.
> Am 10.03.2008 18:48 Uhr schrieb "itsme213" unter <itsme213 at hotmail.com>:
>> Seaside currently keeps a server-side component tree. Each component will
>> typically render a sub-tree of the browser-side DOM tree.
>> What would be the disadvantage of maintaining a unified server-side DOM
>> tree, where some (but not all) nodes would correspond to full-blown Seaside
>> components? I think Michael Lucas-Smith had mentioned this
>> "you tend to want only one tree to represent your UI state on the server,
>> not two - instead of having WAComponent and WABrush separated, we combined
>> the two so that each widget was an element which was also represented on the
>> client side as a DOM node."
>> Thanks - Sophie
>> seaside mailing list
>> seaside at lists.squeakfoundation.org
More information about the seaside