[Seaside] new bindings system

Avi Bryant avi@beta4.com
Sun, 26 May 2002 15:19:18 -0700 (PDT)


So I will probably be releasing this week a Seaside 0.94a, which has a
proposed rework of the bindings system.  In fact the notion of
"bindings" will disappear entirely, to be replaced by an event system.
The WebObjects folks will hate me for this, but I think it'll be
worthwhile for everybody else.  But let me know what y'all think.

Here's the proposal:

- the default behavior for elements will be precisely as it is now,
automagically getting and setting properties via #valueForKey: .
Templates that only use default bindings won't have to change at all.

- The #addBindingsTo: method is replaced with an #addHandlers method.
This has the same purpose as #addBindingsTo: but achieves it slightly
differently.  It works like this:

 - each element and component can accept event handlers by passing blocks
to #onDisplay: and #onUpdate:.  The display handler is called just before
the element or component renders itself to HTML.  The update handler is
called whenever the value of the element has changed.  Both of
these handlers receive the element itself as a parameter.  Together, these
replace bind:toPath: .  For example, what you might have written before as

   (template elementNamed: 'someInput')
     bind: #value toPath: 'item.name'

You would now write as:

   (template elementNamed: 'someInput')
      onDisplay: [:input | input value: item name];
      onUpdate: [:input | item name: input value].

This is more verbose, but it also has (IMO) much clearer semantics.

Attributes such as #bgcolor are handled by modifying the
attributes hash during the display event, eg,

(template elementNamed: 'someRow')
  onDisplay [:row |
   row attributeAt: #bgcolor put: self rowColor].

Elements such as links and submit buttons have an onAction: handler; this
is called in response to the link or button being clicked.  This replaces
#set:toMethod:.  For example, instead of

  (template elementNamed: 'someLink')
    set: #action toMethod: #doStuff

you might write

  (template elementNamed: 'someLink')
    onAction: [:v | self doStuff].

Only one action handler is ever invoked per request.  The action handler
is only invoked after all the update handlers for that page.  This isn't
any different, functionally, from the way action bindings work now, but
again, the semantics are clearer in terms of handlers.

For now at least, the parameter to the action handler is always the top of
the locals stack, the same as the current default for action methods.

- the #defaultBindings method is replaced with the #defaultDisplayEvent
and #defaultUpdateEvent methods.  Override these in your subcomponents to
provide sensible default behavior.

- since typed bindings such as #bind:toNumber: no longer exist, an
inputConversion: method has been added to IATextInput; this allows, for
example,

(template elementNamed: 'numericInput')
  inputConversion: #asNumber

An #outputConversion: or #formatter: or some such will likely also be a
good idea.

One side effect of this proposal is that templates are now cached at the
instance level, not the class level.  This does entail a little more
memory usage and more invocations of the parser.  If it becomes a problem
we can find ways to optimize it.

So, am I off my rocker?  Were the bindings a useful abstraction layer, or
is this proposal a good balance between clarity and power?  From my point
of view, once you're choosing to depart from the automated magic of
associating inputs with fields directly by name, and customizing the
bindings, you might as well be doing it in a more explicit if more verbose
way.

Responses equally welcome from newbies ([JT]im?), old hands (Julian?), and
WO types (Alain? Marcel?)...