Mold: Form Validation for Seaside

A long time ago, I asked about systems for form validation that aren’t “model-based”. By “model-based validation”, I mean that the rules for whether a certain sort of input is acceptable are declared in (or attached to) a domain model that the form is operating on. This is the way that ActiveRecord (Rails) and Magritte (Seaside) work.

I don’t like the whole approach, for reasons I discussed in my earlier post. It breaks down when you want to edit an object in stages, and you end up managing lots of UI-related things down in the model code. But forms are boring and validation can be tedious, which is why frameworks like these have been built in the first place.

What I really wanted was a set of simple helpers that made this work easier — a “less is more” approach to the problem. That didn’t exist (at least in Seaside), so I built my own.

It’s called “Mold”, and it’s available both on SqueakSource and in the Cincom Public Repository. The name is a play on words: it’s a synonym for “form”, but it makes most normal people think of green fuzzy fungae. 

Design Principles

In building Mold, I had several specific goals:

  1. No new components. I didn’t want to have to remember to add anything to #children just because I was using this framework.
  2. Keep track of what the user types, even if it’s not valid, to allow for easy correction of mistakes. “r56” might not parse to a valid integer, but it’s better to let the user delete the “r” than force it to “0” and complain at him.
  3. Emit real objects, not just strings. If I’m asking the user to enter a date or a time, I want a Date or Time object when all is said and done.
  4. Use block-based validation criteria to keep things flexible. Error messages should allow blocks to be used too, so that your can put dynamic behavior in there too.
  5. Correlate errors closely to the problematic fields. It’s more helpful to show an error message right next to the field than to show it at the top of the page.
  6. Strip leading and trailing whitespace, and convert empty strings to nil. Semantically, an empty string almost always means “nothing” and is rarely worth keeping around.
  7. Callback or dictionary-like access to valid data. Sometimes you want to grab bits of data out of the form by name, but most of the time it’s nice to have the form dump valid results right into your model in one step.
  8. Don’t require all-or-nothing use. I might want to use the helpers to build part of the form, but handle other parts myself. It should be possible to completely customize the look of each form without sacrificing the benefits of the framework.

The Basics

To use a mold, you typically instantiate and hold a Mold in an instance variable of your component. For a simple form with no underlying model, you might build the mold when the component is initialized.

initialize
  super initialize.
  self buildMold

For editors with a model under the hood, it makes sense to build the mold when the model is passed in:

account: anAccount
  account := anAccount.
  self buildMold

The mold itself has a canvas-like API for declaring a form’s inputs and validation conditions.

buildMold
  mold := Mold new.
  (mold stringField)
    label: 'Username:';
    on: #username of: account;
    beRequired.
  (mold passwordField)
    label: 'Password:';
    on: #password of: account;
    beRequired. 

In this simple form, we only ask for 2 things, and we hook them directly to accessor methods on the model using #on:of:. This works just like it does in a regular Seaside form, and behind the scenes it simply creates a callback block. You can also create a custom callback block yourself.

When it comes time to render your form, you have to hand the `html` canvas to the mold, and then it gives you lots of handy shortcuts. Basic use will look something like this:

renderContentOn: html
  html form:
    [mold canvas: html.
    mold paragraphs.
    (html submitButton)
      callback: [self save];
      value: 'Save']

This usage tells the mold to render those fields as HTML paragraphs, like so:

Generated with Mold, using the #paragraphs helper

The labels are real HTML <label> tags, and each group is a single paragraph (<p><label for=”…”>Username:</label><br /><input … /></p>) We could have also used #tableRows:

renderContentOn: html
  html form:
    [mold canvas: html.
    html table:
      [mold tableRows.
      html tableRow:
        [html
          tableData;
          tableData: 
            [(html submitButton)
             callback: [self save];
             value: 'Save']]]]

It’s more work to build the framework around the table, but the end result looks like this:

These are the only two “canned” looks for an entire mold, but it’s also possible to take the reins yourself and ask Mold to render single components for a completely custom look. More on that below.

The final step in using this mold is hooking up the #save callback. Let’s assume you’re using the super cool SandstoneDb framework to save your models:

save
  mold isValid ifFalse: [^self].
  account save: [mold save].
  self answer 

That’s all there is to it. The mold can tell you whether its inputs were valid, and if not, it will display error messages on subsequent renders. If it is valid, telling it to save will fire all of its callbacks, thereby applying the changes to the underlying model.

The way I use Glorp, the save method looks nearly identical, but you have to register your model object in a unit of work. Using the mold’s save actions to apply your changes inside the unit of work keeps Glorp’s deep dark change-tracking voodoo working.

save
  mold isValid ifFalse: [^self].
  self database inUnitOfWorkDo:
    [:db |
      db register: account.
      mold save].
  self answer

Improving Looks

Let’s look at what interactions with the mold look like. We declared that both fields should be required, so if you don’t type anything (and just click “save”) you’ll see error messages by each field:

This form is a little bland, and it’s spacing is awkward because Mold uses unordered lists inside those table cells. Let’s apply some simple CSS:

style
  ^'
label.required { font-weight: bold; }
label.required:after { content: "*"; color: red; }
.error { background-color: #ecc; }
.errors { color: red; margin: 0; }
' 

That looks better — we call attention to the required fields, error messages are shown in red, and fields with errors have a reddish background too. Let’s make the username field a little wider. We’ll do this by adding a #customize: block in the mold declaration:

 

buildMold
  mold := Mold new.
  (mold stringField)
    label: 'Username:';
    on: #username of: account;
    customize: [:tag | tag size: 40];
    beRequired.
  (mold passwordField)
    label: 'Password:';
    on: #password of: account;
    beRequired. 

Now the next time we build one of these components (remember, we built the mold when the component was initialized, so it won’t automatically be rebuilt just from a browser refresh), our form will look like this:

 

More Conditions and Inter-Relationships

Let’s modify the field a little further. If we require usernames have to be at least 3 characters long and passwords to have a digit in them, we need some more conditions on these fields. We should also make the user type the password twice to guard against typos.

 

buildMold
  | passwordField confirmPasswordField |
  mold := Mold new.
  (mold stringField)
    label: 'Username:';
    on: #username of: account;
    customize: [:tag | tag size: 40];
    beRequired;
    addCondition: [:input | input size >= 3] labeled: 'Usernames must be at least 3 characters long'.
  (passwordField := mold passwordField)
    label: 'Password:';
    on: #password of: account;
    beRequired;
    addCondition: [:input | input matchesRegex: '.*\d.*']
      labeled: 'Please make sure your password has at least one number in it'.
 (confirmPasswordField := mold passwordField)
    label: 'Confirm Password:';
    beRequired.
  passwordField
    addCondition: [:input | input = confirmPasswordField input]
    labeled: 'Passwords did not match'.
  confirmPasswordField
    addCondition: [:input | input = passwordField input]
    labeled: 'Passwords did not match'. 
There’s a bit more going on here, but it’s all pretty straightforward to use. A few things to note:
  1. We’ve added conditions on required fields, so these won’t be evaluated unless some input is actually given. If these fields were optional, we’d have to check `input` to make sure it wasn’t nil before asking it for its size.
  2. The fields can refer to each other, even out of order. We didn’t technically have to put conditions on both fields, but it makes the error messages look nicer if we do.
  3. There is no callback on the confirmation field. It simply exists for use by the main password field.
  4. When referring to the other fields, we asked them for their #input. This is the string the user typed (having been trimmed of leading and trailing whitespace and converted to nil if it was empty). We could have also asked for its #value, but the value is only valid when the field is valid (incidentally, fields also understand #isValid).

The resulting form looks like this:

One “Gotcha”

Under the hood, the out-of-order field processing is done with a hidden input with a low-priority callback. This is hooked up when the mold’s canvas is set (mold canvas: html). The callback doesn’t fire until all of the other input’s callbacks are processed, but it fires before the action callback from the button.

That means that you must set the mold’s canvas inside the form: [] block. Failure to do so will mean that your validations never get run, and the mold will always answer `true` when sent #isValid.

There might be a cleaner way to do this, but I haven’t found it yet.

Advanced Moldiness

Mold fields can also be given a symbol for a key, which lets you refer to them directly. This makes custom rendering possible, with messages like:

mold paragraph: #confirmPassword.
mold widget: #username
mold errors: #someOtherField

When you use #on:of: to hook up a callback, the key for the widget is automatically assigned to the selector you passed as the first argument. You can also set the key directly using #key:, and a subsequent send of #on:of: will not clobber it.

Error messages can also be blocks, which means that you can put the user’s input into the error message:

(mold stringField)
  key: #username;
  addCondition: [:input | (Account find: [:each | each username = input]) notNil]
    labeled: [:input | 'The username "', input, '" is already taken'].

There’s also no reason why you can’t add two or more molds to a component — say, one for basic settings and one for advanced settings that aren’t shown unless the user clicks “more choices”.

Adding fields is a matter of adding a new protocol on Mold and optionally a new Field subclass. I say “optionally” because some fields can be built as specialized versions of existing fields, like a string field with a default validation rule. The emailField is currently implemented this way.

In Summary

I know not everyone believes that model-based validation is a problem. But if all you’re looking for is a simple way to build a custom form, you might find Mold helpful. We’ve been using it internally for nearly a year, so I figured it was time to touch it up and share it.

Mold makes no requirements of your components or your model objects. It doesn’t use any metadata; it allows you to choose how you want each form rendered; and it doesn’t require you to use it to build the entire form. It’s just a helper, and it gets out of your way when you don’t need it. 

Just don’t leave it alone in a cool dark place for too long, or it might start to grow on you. :)

Advertisements

16 Comments

Filed under Seaside, Smalltalk

16 responses to “Mold: Form Validation for Seaside

  1. Very interesting, I actually found this on SqueakSource today and took a peek at the code, very nice, interesting approach, very OO.

    BTW, I took the #commit: and #commit out of SandstoneDb in the newest versions, now it’s just #save or #save: which is safe because I switched from Semaphores to Monitors which can handle re-entry without deadlocking.

  2. Thanks for the compliments Ramon. Let me know if you find the framework useful in any of your projects or have any ideas for how to make it better.

    I’ve updated the SandstoneDb example above and added a blurb about how we use Mold with Glorp. But we don’t use Glorp unless we’re forced to use a relational store — SandstoneDb cuts out a whole layer of complexity, and I don’t want to go back. Thanks for sharing it with the rest of us.

  3. I actually have my own form builder that I use heavily, takes a much different approach. I didn’t like all the meta data with runtime objects on the class side style Magritte had and it was just too labor intensive to keep it in sync with the accessors on the model’s instance side.

    I want my metadata in my accessors directly, so I use pragmas heavily in the model to specify things like required, validation regex, type, view, label, maxLength, and size. The form builder is sub-classed and at a minimum two methods are overridden to tell the view what to render. Here’s a basic example of a form…

    actions
    ^#(save cancel)

    fields
    ^ #(#title #body)

    That’s it. That info is used to reflect over the model, which may be a domain model, or may be the form itself by sending #beOwnModel in initialize and creating your accessors directly in the view.

    The actions generate buttons on the form and invoke methods of the same name, #save and #cancel are built in actions; save just commits the changes from a memento to the model once all validation rules are satisfied.

    In the form, #validate can be overridden to add additional validation rules to the form beyond what the model can specify such as matching two passwords, or making sure a user doesn’t already exist. Those rules belong in the view not the model.

    Most importantly, with a few naming patterns, you can create rendering methods that step in and replace the dynamically generated ones and drop into raw Seaside to customize things that are beyond what a generic field might look like. Implementing #renderTitleOn: would cause that render method to be called for the #title field rather than dynamically generating one and you can do whatever customization you need, if you must.

    If the changes you need are small, you can just hook the creation of the generated tag by implementing #onTitleTag: where you’ll be passed the generated tag directly allowing you to send additional messages, like #onClick: or #optionalLabel: or whatever you want to do to that tag. This allows you to customize the dynamically rendered form on the fly without having to write any of the rendering code.

    My approach relies heavily on pragmas and reflective magic, but requires me to write much less code than any other approach I’ve yet to encounter. I got the idea from looking at the Sushi Store forms and seeing how Avi did basically the same thing with WALabelledFormDialog. I just took it a bit farther and coming from Magritte, I liked metadata so I married the two approaches went with pragmas for the metadata.

  4. BTW, because Seaside is stateful, I can implement wizards by passing the model through several forms each editing a subset of the models fields and only saving the active record to disk on the final form.

  5. Interesting approach too, I didn’t realize you had moved beyond Magritte. I should check comments on your blog more often.

    You said that yours is a very different approach, but I think the general approach is more similar than different. In both cases, there is a sort of specification declared (in pragmas, or in a buildMold method) and then there are helpers (your form builder class, or Mold’s “macros” on the html canvas) that build and hook up the form while letting you override aspects of the rendering. For wizards, I’m doing the same thing you are; and for custom validations, again it’s the same approach: just implement the validation code in the form component.

    Granted, I haven’t looked at your code yet, so maybe I don’t know what I’m talking about. :) I guess the main underlying similarity I’m seeing is that the model doesn’t drive the process, it just assists in it.

    The main difference here seems to be where you put the declaration. I’m still not sold on metadata in the models, but I can see where your framework makes it easier to build multiple forms on top of the same model. Most of my forms end up asking the user for the same types of data, which is why Mold is built around a concept like “now prompt them for a percentage” rather than “now prompt them for #completion, according to whatever its rules are”.

  6. Certainly there are similarities, they’re both doing the same task, building forms. The key difference is in the metadata and the html generation.

    Your metadata is generally specified in the view while building the mold using plain old objects, not that dissimilar from Magritte except of course it’s in the model. However, unlike Magritte, your metadata and the views that render the fields are one and the same while Magritte has a separate hierarchy of metadata (descriptions) and view components (overly complex IMHO).

    Your mold views however, unlike Magritte, are pluggable, allowing a #customize: block to be sent to give you access to the tag while it’s being generated, very nice as that sidesteps the need to have to subclass and customize the views (why I don’t like Magritte’s approach). I do the same thing but using #respondsTo: and method naming conventions. In either case, exactly what you need to Ajax’ify up a form.

    The only thing that seems off to me is the setting of the canvas on the mold and mold itself just being a helper class who does most of the work of the view. Looking at it, I think I’d make mold a subclass of WAComponent and do this…

    Mold>>renderContentOn: html
    html := html.
    self renderMoldOn: html.

    Mold>>renderMoldOn: html
    self subclassResponsibility

    So the form itself is the mold and the view…

    MyForm>>initialize
    | email confirmEmail |
    email := self emailField
    key: #email;
    label: ‘Email Address:’.
    confirmEmail := self emailField
    key: #confirmEmail;
    label: ‘Confirm Email:’.
    email
    addCondition: [ :input | input = confirmEmail input ]
    labeled: ‘Email addresses did not match.’.
    confirmEmail
    addCondition: [ :input | input = email input ]
    labeled: ‘Email addresses did not match.’.

    MyForm>>renderMoldOn: html
    html form:
    [ html table: [ self tableRows ] ].
    html submitButton
    value: ‘Submit Form’;
    callback: [ self isValid ifTrue: [ self save ] ] ]

    But I could be overlooking some benefit of it being just a helper rather than the view.

    In any case, I like your approach much better than Magritte because I think pluggable views are much easier to customize. But keeping the metadata in the view does pose the problem of duplicating it across multiple views.

    I do like your field hierarchy, it’s much more OO feeling. Mine is a single class with a copy of the domain object as a memento for holding possibly invalid state and extentions to all the #on:of: holders in Seaside adding #on:of:type: which runs any submitted values through a type converter class before writing to the model. This class looks sort of like a visitor but the type name is used with reflection to invoke the correct conversion method. So rather than creating my own hierarchy of tags, I just extended the tags in Seaside to be type aware and keep all the validations in the form class itself. In any case, nice code, always enjoy seeing how someone else tackles a problem.

    BTW, one of the reasons I like pragmas so much is that it’s a cross platform approach, most languages support some form of pragma regardless of what they call them, so it allows me to work in a consistent style across C#, VB.Net, and Smalltalk by tagging metadata onto classes and using reflection to access it.

  7. I thought of making a component like that, but I didn’t want to force myself into subclassing that every time I wanted to build a form, especially if I’m already at WAComponent > MyAppComponent > WizardStep > ContactInfoStep or something like that. I tend to try to milk too much re-use out of the inheritance hierarchy anyway.

    I could also imagine situations where it’d be handy to have two molds within one form, though in practice I haven’t done that much.

    The canvas: setting bugs me too but I haven’t found a better way around it yet. I considered using a WADynamicVariable, or passing the canvas around as an argument to every method along the way, but both of those just added annoying noise to the code.

    One other thing I forgot to mention about #customize: — the other option for customization in Mold is to send messages to the tag returned by #widget: — it’s just a tag brush, so something like this is perfectly legit:

    (mold widget: #email) size: 30.

    You don’t have this option when you’re using the more broad-sweeping helper methods like #tableRows or #paragraphs, of course.

    Again, thanks for the compliments. I have long admired your solid thinking and the clear explanations you write up on your blog, so it means a lot to me.

  8. No prob, and thanks, glad you like the blog. I hope to see you blog more about Seaside, still not enough of us doing it, in depth at least. If you have any more stuff you’re considering writing up, please do, my Google Reader is starving for interesting Smalltalk content.

  9. Leandro

    Hello Ken, I’m giving a try to Mold. Do you have a mailing list or something like that to discuss mold related stuff?
    I have some questions and I’m sure more will arise..
    thanks.

  10. Cédrick

    Hi Ken,

    I’m also giving mold a try. I loaded it in seaside 2.9. No loading problem. On pharo, only #trimBlanks is missing. I used #trimBoth wich is definied in seaside (trim left and right blanks).

    Problem is that validation doesn’t occur. I changed #priority from 7 to 3 and now it seems to work.

    Anyway thanks for sharing it.

  11. Stephan Eggermont

    Seaside Issue: 337
    replace storeCallback: by store: in Mold canvas:

  12. Stephan Eggermont

    Seaside issue: 64
    replace trimBlanks by trimBoth

  13. RD

    Hello, I use Mold to generate the forms I use for an editor, but when I try to submit the corresponding fields using triggerForm: the values applied using mold save are always nil. What can be the reason for that?

  14. Pingback: Forms in Seaside and Iliad: helpers to make life easier « Joachims Small World

  15. Thanks to share this nice package. Its design makes it easy to understand and to extend if necessary.

    I found your package on the SmalltalkHub repository at http://smalltalkhub.com/#!/~philippeback/Mold

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s