[Seaside] Embedding Components Documentation

Avi Bryant avi at beta4.com
Thu May 13 02:14:35 CEST 2004


On May 12, 2004, at 12:34 AM, Bill Holloway wrote:

> In the online tutorial on the SeaSide swiki, near the bottom it says:
>
> "The details of embedding components are beyond the scope of this
> tutorial..."
>
> Is there any documentation of such embedding (beyond the store 
> tutorial)?

No, but here are some general notes:

- It is common for a component to display instances of other components 
while rendering itself.  It does this by passing them into the #render: 
method of WAHtmlRenderer.  For example, this #renderContentOn: method 
simply renders a heading and then displays a counter component 
immediately below it:

renderContentOn: html
	html heading: 'My Counter' level: 3.
	html render: myCounter.

It's important that you use #render:, rather than directly calling the 
#renderContentOn: method of the subcomponent.  The following is *not* a 
supported technique:

renderContentOn: html
	html heading: 'My Counter' level: 3.
	myCounter renderContentOn: html.   "DON'T DO THIS".


- These instances, or "subcomponents" are usually instance variables of 
the component that is "embedding" them.  They are commonly created as 
part of the parent's #initialize method:

initialize
	myCounter := WACounter new.

They may also be stored in a collection.  One fairly common pattern is 
to keep a lazily initialized dictionary of subcomponents that match a 
collection of model items.  For example, if you wanted a BudgetItemRow 
subcomponent for each member of budgetItems, you might do something 
like this:

initialize
	budgetRows := Dictionary new.

rowForItem: anItem
	^ budgetRows at: anItem ifAbsentPut: [BudgetItemRow item: anItem]

renderContentOn: html
	self budgetItems
		do: [:ea |  html render: (self rowForItem: ea)]
		separatedBy: [html horizontalLine]

- Each parent component *must* implement a #children method that 
returns a collection of all of the subcomponents that it might display 
on the next render.  For the above two examples, #children might look 
like this:

children
	^ Array with: myCounter

or this:

children
	^ self budgetItems collect: [:ea | self rowForItem: ea]

- If a subcomponent makes a #call: to another component, that component 
will appear in place of the subcomponent.  In the first example, if 
myCounter made a #call: to DateSelector, that DateSelector would appear 
in the context of the counter's parent, with the 'My Counter' heading 
above it.

- Since a subcomponent has not been #call:'d, in general #answer: is a 
no-op.  However, the parent may attach an #onAnswer: block to the 
subcomponent to be notified if it sends #answer:.  This allows one 
component to be used both from #call: and through embedding.  For 
example:

initialize
	dateSelector := WADateSelector new onAnswer: [:d | self dateChosen: d].

Cheers,
Avi



More information about the Seaside mailing list