[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