Dynamic Web Development with Seaside

12.2Example: Embedding an Editor

We will build a new variation of our contact list manager. What we’d like to do is adapt our contact manager so that we see the item editor on the same page as the contact list item. That is, we want to embed the editor on the same page as the address list itself. While we could adapt the previous component to embed a component, we prefer to define a new component from scratch.

Embedding a ContactView into another component

We already have a working editor component so let’s just add it to a new IAddress component. That is, we’re going to embed the ContactView component within the IAddress component.

Create the class. First we create the class of the new component IAddress.

WAComponent subclass: #IAddress
instanceVariableNames: 'editor'
classVariableNames: ''
poolDictionaries: ''
category: 'iAddress'

We add an instance variable, editor, to the class IAddress which is a reference to the editor that we will embed within our IAddress component. It is not always necessary to maintain a reference to an embedded component: we could also create the component on the fly (as soon as it is returned as part of the component’ children). In our case, since the elements of our application are stateful objects, it is better to reuse components, taking advantage of the fact that they can store state, rather than recreating them. We will revisit this issue in Section 12.7.

Initialize instances. The initialize method creates the editor and gives it a default contact to edit. We can then reuse the editor to edit other contacts, avoiding the need to create a new editor every time we want to edit something.

IAddress>>contacts
^ Contact contacts
IAddress>>initialize
super initialize.
editor := ContactView new.
editor contact: self contacts first

Specifying the component’s children. Any component that uses embedded children components should make Seaside aware of this by returning an array of those components. This is necessary because Seaside needs to be able to figure out what components are embedded within your component; Seaside needs to process all the callbacks for all of the components that may be displayed, before it starts any rendering of those components. Unless you add the children components to the parent’s children method, the first Seaside will know about your children components is when you reference them in your rendering methods.

Specify the children. Here is how to define the method children which returns an array containing the editor that is accessible via the instance variable editor.

IAddress>>children
^ Array with: editor

Specify some actions. Now define methods to create, add, remove and edit a contact.

IAddress>>addContact: aContact
Contact addContact: aContact
IAddress>>askAndCreateContact
| name emailAddress |
name := self request: 'Name'.
emailAddress := self request: 'Email address'.
self addContact: (Contact name: name emailAddress: emailAddress)
IAddress>>editContact: aContact
editor contact: aContact
IAddress>>removeContact: aContact
(self confirm: 'Are you sure that you want to remove this contact?')
ifTrue: [ Contact removeContact: aContact ]

Embedding a ContactView into another component with Halos

Some rendering methods. We use a table to render the current contact list and let the user edit a contact by clicking on the name link.

IAddress>>renderContentOn: html
html form: [
self renderTitleOn: html.
self renderBarOn: html.
html table: [
html tableRow: [
html tableHeading: 'Name'.
html tableHeading: 'Address' ].
self renderDatabaseRowsOn: html ].
html horizontalRule.
html render: editor ]
IAddress>>renderTitleOn: html
html heading level: 2; with: 'iAddress'
IAddress>>renderBarOn: html
html anchor
callback: [ self askAndCreateContact ];
with: 'Add contact'
IAddress>>renderDatabaseRowsOn: html
self contacts do: [ :contact |
html tableRow: [ self renderContact: contact on: html ] ]
IAddress>>renderContact: aContact on: html
html tableData: [
html anchor
callback: [ self editContact: aContact ];
with: aContact name].
html tableData: aContact emailAddress.
html tableData: [
html anchor
callback: [ self removeContact: aContact ];
with: '--' ]

Register the application as "iAddress" and try it out. Make sure that the editor is doing its job. Activate the halos. You’ll notice that there is a separate embedded halo around the editor component, see Figure 86. It is very helpful to inspect the state of a component in a running application (or view the rendered HTML.)

Our simple implementation of IAddress>>editContact: will save changes even when you press cancel. See Section 11.5 to understand how you can change this.

Copyright © 19 March 2024 Stéphane Ducasse, Lukas Renggli, C. David Shaffer, Rick Zaccone
This book is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 license.

This book is published using Seaside, Magritte and the Pier book publishing engine.