So far, we have seen how a component displays itself and how a component can invoke another one. This component invocation has behaved like a modal interface in which you can interact only with one dialog at a time. Now, we will demonstrate the real power of Seaside: creating an application by simply plugging together components which may have been independently developed. How do we get several components to display on the same page? By simply having a component identify its subcomponents. This is done by implementing the
Suppose that we would like to add an item to our list. Normally a web application developer would use a single form which would be used both to edit and to add a todo item, but for demonstration purposes we take a different approach. We would like to display the editor below the list. That is, we want to embed a
ToDoItemView in a
ToDoListView. Our solution is to allow the user to add an item by pressing a button which will display an editor for the new item, as seen in Figure 105.
We begin by adding an instance variable named
editor to the
ToDoListView class as follows:
WAComponent subclass: #ToDoListView
Then, we define the method
children that returns an array containing all the subcomponents of our component. This array contains just the element
editor since list items are rendered by the list component itself. Note that Seaside automatically ignores component children that are nil, so we don’t have to worry if it is not initialized.
^ Array with: editor
renderContentOn: to add an Add button and to trigger the add action. Note that when the value of the instance variable
editor is nil the rendering does not show anything.
html heading: self model title.
html form: [
html unorderedList: [ self renderItemsOn: html ].
callback: [ self add ];
text: 'Add' ].
html render: editor
addto add a new component. It first creates an instance of
ToDoItemViewwhose model is a newly created todo item.
editor := ToDoItemView new model: ToDoItem new
answer: messages. How do we update the todo list model? Suppose the user cancels the editing. How do we handle that situation? We need a way to know when a subcomponent executed the method. You can get notified of
answer: execution by using the method
onAnswer: involves attaching a handler from the parent once the child component is instantiated. The method
onAnswer: requires a block whose argument represents the object that got answered (
parent onAnswer: [ :object | ... ]).
onAnswer: block will be executed with the answered object as its argument. Since the editor will return
nil when the user cancels editing, we need to check the value passed in. We modify the
add method as follows:
editor := ToDoItemView new model: ToDoItem new.
editor onAnswer: [ :value |
ifFalse: [ self model add: value ].
editor := nil ]
If you get the error "Children not found while processing callbacks", check that the
children method returns all the direct subcomponents. The halos are another good tool for understanding the nesting and structure of components. We suggest you turn on the halos while developing your applications, as seen in Figure 107.