http://lists.squeakfoundation.org/pipermail/seaside/2005-June/005260.html

From: Avi Bryant Subject: [Seaside] new Canvas rendering

On 6/20/05, Jason Rogers <jacaetevha at gmail.com> wrote:

Avi,

In a few recent posts you have made allusions to the new rendering
style using Canvas. Can you summarize for us what that is (what it
does, how it differs, etc.)?

Certainly. I've posted about it before, but I don't mind posting again, especially since it's actually in a usable state now.

The basic problem that Canvas is trying to solve is that of combinatorial explosion in the protocol of WAHtmlRenderer. Consider something like a text input. The original, basic method for rendering a text input was #textInputWithValue:callback:

html textInputWithValue: person name callback: [:v | person name: v]

But there's also a variation using the …On:of: pattern:

html textInputOn: #name of: person

And a variation with a liveUpdate callback:

html textInputWithValue: person name callback: [:v | person name: v] liveCallback: [:r :v | …]

Now what if we want to use both of these variations at once? Do we also need #textInputOn:of:liveCallback:? What about #passwordInputOn:of:liveCallback:? And so on. It's relatively easy to add new convenience methods, but it's a huge pain to combine them, even when (as in this case) they're totally orthogonal.

The Canvas renderer is just a slight change to the API style that makes this kind of combination much easier. It divides what would have been a single method on HtmlRenderer into three steps: first, you tell the canvas what kind of “brush” (tag, usually) you want to use. For example, the method #textInput will return a new instance of WATextInputTag. Then, you use protocol specific to that brush to configure it: for example, you might send it some combination of #value:, #callback:, #liveCallback:, #on:of:, etc. You almost never need to use temps when doing this, but can do it with cascades instead:

html textInput value: person name; callback: [:v | person name: v]

or

html textInput on: #name of: person

or

html textInput on: #name of: person; liveCallback: [:r :n | ….]

The third step is to render any contents or children (if any) of this tag. You do this by passing some renderable (often a block) to the method #with:. A text input wouldn't have any, but consider something like a table:

html tableRow rowSpan: 3; with: [html tableData with: [html bold with: person name]. html tableData colSpan: 2; with: [….]]

Note that instead of using #attributeAt:put: before the element, as in HtmlRenderer, attributes are set as part of the configuration step - and so classes like WATableRow can implement convenience methods for common attributes like #rowSpan: as needed.

If no configuration is needed, you can combine the first and third steps by using a keyword message instead of a unary message to specify the brush type, which is more compact:

html tableRow rowSpan: 3 with: [html tableData: [html bold: person name]. html tableData ….]

This style of API still allows streaming - the HTML for the open tag is generated as soon as #with: is sent. If #with: is never sent, it's triggered automatically (with an empty block) whenever the next tag is started. That means #with: does have to be the last thing you send, any configuration done afterwards will have no effect.

Sometimes a temp is handy:

myDiv := html div. self useSpecialClass ifTrue: [myDiv class: 'specialClass']. myDiv with: [html text: ….]

But bear in mind that these are basically temporary objects - it won't do you any good to stash them in an ivar and try to use them later, for example.

The Canvas implementation in recent 2.6a versions is relatively complete (we're using it on one of the projects I'm working on), but there are no doubt still lots of holes to patch up and conveniences to add, so feel free to pitch in.

Does that help?

Avi

 
seaside_canvas_api.txt · Last modified: 2007/08/01 13:16 by eric
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki