[Seaside] Component tree vs. DOM tree

Michael Lucas-Smith mlucas-smith at cincom.com
Tue Mar 11 12:56:17 UTC 2008


Andreas Tönne wrote:
> Sophie,
>
> 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
> DOM manipulating JavaScript?)
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 
'javascript'.

Over the last several years we've seen an uptake in peoples willingness 
to use this 'javascript' language in more sophisticated ways to take 
advantage of advances in the web browsers capabilities. Firefox 3b4 for 
example has sped up Javascript execution significantly in an effort to 
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 
Javascript as our drivers.

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 
philosophy.

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 
routine (css) and customizable interaction logic (javascript). So this 
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 
javascript.

The javascript can then update as many widgets on the client side as it 
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 
is emitted back to the client as javascript to run. At the same time, 
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:

call: aComponent
 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 
javascript too.. that allows bookmarkability and entry point definition. 
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 
Mootools to reduce the cost of writing javascript and smalltalk code 
that communicated to each other. In fact, if the Smalltalk code sent a 
message to the javascript code, it would json the arguments and emit the 
javascript call to the client immediately, get a response and turn it 
back in to smalltalk objects. Vice versa, the Javascript code could call 
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:
a) Chat/IM
b) Updating screen as results came back from a database query in the 
background
c) Clock

> 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 
to render itself out as javascript as a change regardless. The 
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.

Cheers,
Michael
> Andreas
>
>
> 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
>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>     
>
>   



More information about the seaside mailing list