Scriptacu-list Tutorial(Part 2)
So we’ve got our first cut domain model and now its time to add our views. Today we are going to add another two classes MyAddressBookView and MyContactEditor…
MyAddressBookView
- We begin by creating MyAddressBookView as a sublcass of WAComponent with an instance variable of addressBook. (NB by using an instance variable it will mean that your address book gets reset for every session. If you’re not happy with that you can use a class variable instead)
- Once again we will lazy initialize our variable
addressBook
^addressBook ifNil: [addressBook := MyAddressBook new]
- Now we will create our rendering method. We start simple by coding a heading…
renderContentOn: html
html heading: 'Contacts' level: 1. - Then we add some more lines that will iterate over our collection of contacts and for each one display their name as a hyperlink to a page where their details can be edited…
self addressBook
contacts do: [:contact |
html div class: ('row');
with: [
html anchor
callback: [self showContact: contact];
with: (contact firstName , ' ', contact surname )]] - Lastly we will add a few lines to create a button which will allow us to create a new Contact…
html form: [
html span class: 'button';
with: [html submitButton on: #addContact of: self]](Note here the the on: aSymbol of: anObject is a convenience method that uses aSymbol to derive a label for the button and the method to perform. Neat eh!)
- Before this code can run we need to define the two methods that called in the renderContentOn: method above. First we will define the addContact: method
addContact
| contactEditor contact |
contact := MyContact new.
contactEditor := MyContactEditor on: contact.
(self call: contactEditor) ifTrue: [self addressBook addContact: contact]…this is all pretty straightforward except to point out that we will only add our new contact if the editor replies true (as a result of clicking save)
- Finally for this class we will add our showContact: method…
showContact: aContact
self call: (MyContactEditor on: aContact)…managing save and cancel for new contacts is easy. For editing it is a little more involved. Next time we will alter this method to implement a memento for the contact
MyContactEditor
- Now we need to create the editor for our contacts. Again we create MyContactEditor as a subclass of WAComponent this time with an instance variable of contact.
- Now create the accessors for the contact variable like we did for the variables in our domain classes last time.
- We are now going to create a class method that will deliver us a new Editor with the contact already populated…
on: aContact
^self new
contact: aContact ;
yourself - One of the most powerful things about seaside is the ability to factor your html into small callable methods. When creating a form I often repeat the same pattern for each input field; I create a div; I put a label for the field inside a “label” span; I put an input field inside a “field” span. So lets create a method that will do that for us…
renderLabel: aLabel withInput: anInputFieldBlock on: html
html div class: 'row';
with: [
html span
class: 'label';
with: aLabel .
html span
class: 'field';
with: anInputFieldBlock]. - Now we can use this in our rendering…
renderContentOn: html
html heading: 'Contact Details'.
html form
with:
[
self renderLabel: 'First Name: ' withInput: [html textInput on: #firstName of: contact] on: html.
self renderLabel: 'Surname: ' withInput: [html textInput on: #surname of: contact] on: html.
self renderLabel: 'Date of Birth: ' withInput: [html dateInput on: #dateOfBirth of: contact] on: html.
self renderLabel: 'Telephone: ' withInput: [html textInput on: #telephoneNumber of: contact] on: html.
self renderLabel: 'Email: ' withInput: [html textInput on: #emailAddress of: contact] on: html.
html span class: 'button';
with: [html submitButton on: #save of: self].
html span class: 'button';
with: [html submitButton on: #cancel of: self].
] - Finally we need to create the save and cancel methods called by our buttons…
save
self answer: truecancel
self answer: false
If you got through all that OK then open up a workspace and evaluate MyAddressBookView registerAsApplication: 'AddressBook' and hopefully you can then try out your app (most probably on http://localhost:8080/seaside/AddressBook)
January 18, 2007 at 7:03 pm
This will be a neat blog thanks for taking the time to put it up. I tried entering the example but get a walk back as MyContactEditor>>contact: was missing. Hope I didn’t miss something.
Steve
January 19, 2007 at 12:57 am
Nice example, good to see people groking Seaside, you hit a major point with #4, well factored html, something you just can’t do in other frameworks.
One thing to consider, on a per project basis, if you have things you’d like available everywhere, you can subclass WACanvasRenderer, add those helpers to your own rendering class, and then override the factory method that creates the rendering component to have your custom renderer passed to all components, making something like #renderLabel available everywhere, not just on self.
Also, components answer nil by default, so you can just return your object when successful, allowing this..
addContact
(self call: (MyContactEditor on: MyContact new))
ifNotNilDo: [:contact | self addressBook addContact: contact]
January 19, 2007 at 4:20 pm
Steve
I didn’t provide the code for the contact accessors referred to in step 2. If you add a setter method like…
contact: aContact
contact := aContact
…all should be well.
January 19, 2007 at 4:22 pm
Ramon
Thanks for looking in and taking the time to comment. It’s good to have someone with a good knowledge of seaside helping out with some “tricks of the trade”
September 6, 2007 at 5:14 pm
Hi, do you have a downloadable package that consists of the individual parts of the 7 part tutorial?
At this time, I’m getting the error message on part 2 of the tutorial in regards to the #renderContentOn: method. BTW, I’m using Seaside 2.8 Beta with VisualWorks 7.5.
Thanks in advance,
-Conrad
September 7, 2007 at 8:45 am
Hi Conrad
I just saw your stack trace on the seaside mailing list.
It looks as though the contacts OrderedCollection has boolean values in it rather than MyContact instances. I would suspect that the cause of the error is in the #addContact method.
If you have used Ramon’s code you will also have to change the #save method on MyContactEditor so that instead of returning ‘true’ it returns the contact