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.
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
Create the class. First we create the class of the new component
WAComponent subclass: #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.
^ Contact contacts
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
^ Array with: editor
Specify some actions. Now define methods to create, add, remove and edit a contact.
Contact addContact: aContact
| name emailAddress |
name := self request: 'Name'.
emailAddress := self request: 'Email address'.
self addContact: (Contact name: name emailAddress: emailAddress)
editor contact: aContact
(self confirm: 'Are you sure that you want to remove this contact?')
ifTrue: [ Contact removeContact: aContact ]
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.
html form: [
self renderTitleOn: html.
self renderBarOn: html.
html table: [
html tableRow: [
html tableHeading: 'Name'.
html tableHeading: 'Address' ].
self renderDatabaseRowsOn: html ].
html render: editor ]
html heading level: 2; with: 'iAddress'
callback: [ self askAndCreateContact ];
with: 'Add contact'
self contacts do: [ :contact |
html tableRow: [ self renderContact: contact on: html ] ]
IAddress>>renderContact: aContact on: html
html tableData: [
callback: [ self editContact: aContact ];
with: aContact name].
html tableData: aContact emailAddress.
html tableData: [
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.