Scriptacu-list Tutorial(Part 3)

So we’ve got a simple Contact List working but there are two big problems.

  1. When I’m editing the contact the data changes even if I click “Cancel”.
  2. It doesn’t look very pretty.

Today we are going to fix this…

A Little Memento

Our problem with the data changing even when we click cancel is a common one in “pass by reference” object oriented languages. In the editor view you are working with exactly the same object as you were looking at in the list view.

We can solve this with a Design Pattern called “Memento”. There are a few different ways of implementing Memento in Smalltalk (including the use of #become:, which I love to show to Java programmers but generally don’t recommend implementing in real systems). What I will present here is a use of Memento that goes like this.

  1. From the list view the user selects an Contact to edit.
  2. The System takes a copy of the Contact‘s current state (a memento).
  3. The System opens an editor on the original Contact
  4. If the user clicks “Save” the system simply returns to the list view as all the data changes happened in the editor.
  5. If the user clicks “Cancel” then we copy back the stored data from our memento into our Contact

(NB Some people prefer to let the editor work on the copy rather than the original object and then do nothing if Canceled or copy the new state if Saved)

So on to the code. First we need replace #showContact: in MyAddressBookView with…

showContact: aContact
| contactEditor memento|
memento := aContact copy.
contactEditor := MyContactEditor on: aContact.
(self call: contactEditor) ifFalse: [aContact resetFromMemento: memento]

Then we need to create a new #resetFromMemento: method in MyContact…

resetFromMemento: aMemento
dateOfBirth := aMemento dateOfBirth.
emailAddress := aMemento emailAddress.
firstName := aMemento firstName.
surname := aMemento surname.
telephoneNumber := aMemento telephoneNumber.

…and that’s all there is to it.

Improving my Looks

Before we can really show off our new app we need to get rid of that clunky browser default font and make it look a bit nicer. We will do this by adding a very simple CSS stylesheet. (I’m not a graphic artist or css expert so please don’t expect too much of this).

Seaside lets you code your CSS in a number of different ways but I concur with Ramon Leon’s conclusions that externalising CSS to a file is best. So here’s our CSS…

body { background-color: #fff;
font-family: "Trebuchet MS"}
padding: 2px;
.row1 {
background-color: #99d ;
color: #fff;
.row0 {
background-color: #67d ;
color: #fff;
.row {
background-color: #67d ;
color: #fff;
color: #ccf;
font-weight: bold;
text-decoration: none;
color: orange;

…which we need to save as “main.css” on the document root of our application. (This is most probably in the directory from which you startup squeak unless you have reconfigured your web server. See David Shaffer’s tutorial for more on configuring the web server)

We now need to make our web pages include a link to the CSS. We can achieve this by overriding the #updateRoot: method on our MyAddressBookView and our MyContactEditor as follows…

updateRoot: aRoot
super updateRoot: aRoot.
aRoot linkToStyle: '/main.css'

…but since you are adding the same method to two classes you really should refactor the hierarchy here by introducing a MyAddressBookBaseView class as a subclass of WAComponent. You can then make MyAddressBookView and MyContactEditor subclass this new class and move #updateRoot: into it.

Homework  !

So we now have an application that works and looks good too! (Well it will if you put a bit more effort int the CSS :-)).

Your homework for today is to grab the nearest Java or .Net developer and challenge them to build the same app as quickly as you have done!

Explore posts in the same categories: Design Patterns, Objects, Seaside, Smalltalk

11 Comments on “Scriptacu-list Tutorial(Part 3)”

  1. Michel Bany Says:

    It is also possible to use:

    html cancelButton on: #cancel of: self

    This suppresses the evaluation of the input callbacks and therefore leaves the model unchanged.

  2. Hi Michel

    This sounds very useful but I can’t find an implementation of #cancelButton on WARenderCanvas. Is this an extension you have written or am I missing something?

  3. jRave Says:

    Using your method of reverting back to copy seems dangerous to me as for a split second the model will actually contain invalid data. It depends on persistence mechanism you are using, but even in case you are using a persistence layer requiring commit to actually write the data and -in case caching is implemented- passing only copies of cached data this seems as a bad practice to me, it can bite you later, and you won’t know where it came from …

    Either editing the copy, or even better using the method Michel B. suggested seems as a better alternative to me – WARenderCanvas responds to cancelButton, at least in 2.7a1-lr.139 I’m using.

    Also, readers beware: This example answers a _reference_ to an collection used internally in a class. This is in many cases now what you want, as it can lead to unwanted side-effects. Preferred solution is ansvering a copy of the collection and delegating the needed functionality (ie. adding/removig etc.) as such:

    “Lazy initialization is pointless here. Use it in cases where the performance hit of allways having to send a ifNil: message overcomes the odds of having a large/long initialization deffered until needed”
    ^ (contacts ifNil: [contacts := OrderedCollection new]) copy

    addContact: aContact
    contacts add: aContact

    remove: aContact
    contacts remove: aContact

  4. I’ve just updated my version of Seaside it seems #cancelButton was missing from the verision of Seaside I started my investigations on. You’re right that this is a much better option in this case.

    The memento pattern however is useful to investigate as it has many potential applications. The decision to use an “optimistic” update approach as outlined in the tutorial vs a “pessimistic” approach as you describe is not a “one size fits all” question. It is dependent on your persistence mechanism and your performance requirements (E.g. the cost of rollback vs the cost of commit)

    Lazy initialization is a bit of a personal preference of mine – I cannot remember a single case in any of my apps where the added cost of the #ifNil:has been significant whereas I have hit problems on a number of occasions with badly performing #initialize methods. I sure other may have anecdotes that lead them to prefer the opposite approach 🙂

    You will note that I did provide #addContact: method on MyAddressBook in Session 1 (I left #removeContact: out of scope for the tutorial). You are correct that there are dangers *if* you can’t trust your clients to use the promoted interface on the containing class but there are an equal number of dangers with always return a copy of a collection (profusion of object references, inconsistent state etc.)

  5. jRave Says:

    errata 😉 :
    _reference_ to an collection -> reference to collection
    This is in many cases now what you want -> This is in many cases _not_ what you want

    “Lazy initialization is pointless here. Use it in cases where the performance hit of allways having to send a ifNil: message overcomes the odds of having a large/long initialization deffered until needed” -> “Lazy initialization is pointless here. Use it in cases where the performance hit of _allways_ having to send a ifNil: message is neglectible in comparation with large and not everytime needed allocation (or if it speeds up startup of your application)” (WTF i was trying to say here ? 😉

  6. jRave Says:

    of course one size does not fit all.
    i was just offering a look from another point-of-view, so people will be aware of odds and benefits of both of those particular approaches (and perhaps make better informed decission in future ;-): copying costs more, but is safer. lazy initialization is easier/safer, but if you access a particular property 1000 times, its a 1000 added method calls – i don’t know if it makes such a difference, but since “normal” initialization does not bother me I prefer not to add overhead.

    This article is IMHO good example of Seaside’s AJAX capabilities, but as a lot of people tends to copy code without thinking I tried to warn them so they wouldn’t have to learn all that the hard way (which, OTOH, migh be better at times).

  7. I appreciate your input jRave. It’s been good to see a number of more experienced seasiders adding their insights to the blog. Hopefully people will find as much if not more to learn in the comments than they to from the tutorial itself 🙂

  8. Jonny Cundall Says:


    aRoot linkToStyle: ‘/main.css’

    does not work in Seaside 2.8, try

    aRoot stylesheet resourceURL ‘/main.css’

    instead if using this version.

  9. I don’t think the following syntax is available anymore in the current Seaside 2.8:

    aRoot linkToStyle:

    Use this instead:

    aRoot stylesheet url: ‘/main.css’.

  10. Hmmm… Are you trying to be with my cheery trading Sorry, for off top, i wanna tell one joke) Why did the garbage look sad? Because it was down in the dumps.

  11. GAGECLOUG Says:

    Ordinary row hosting service Rapidshare ( ) has been slapped with a $33.4 million outstanding nigh a German court and ordered to espouse stricter regulations in search its uploaded content, according to not too reports.

    The lawsuit was brought past German royalties collector GEMA, who called on the Regional Court in Hamburg to organization the march hosting usage to a halt it from hosting 5,000 music tracks on its put for download.

    GEMA released a report in German addressing the court’s ruling:

    “The judgment states that the hosting service itself is now chief as a service to making persuaded that not any of the music tracks worried are distributed via its programme in the future. This means that the copyright holder is no longer required to perform the ongoing and complex checks.”

    The court concluded that Rapidshare and other like queue sharing sites had not infatuated the proper measures to impede copyright infringement from occurring via the service

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: