6 Formlets: Functional Form Abstraction
The Web Server provides a kind of Web form abstraction called a formlet.
Formlets originate in the work of the Links research group in
their paper The Essence of Form Abstraction.
6.1 Basic Formlet Usage
Suppose we want to create an abstraction of entering a date in an HTML form. The following
formlet captures this idea:
The first part of the formlet syntax is the template of an X-expression that is the rendering
of the formlet. It can contain elements like ,(=> formlet name) where formlet
is a formlet expression and name is an identifier bound in the second part of the formlet
syntax.
This formlet is displayed (with formlet-display) as the following X-expression forest (list):
(list |
'(div "Month:" (input ([name "input_0"])) |
"Day:" (input ([name "input_1"])))) |
date-formlet not only captures the rendering of the form, but also the request processing
logic. If we send it an HTTP request with bindings for "input_0" to "10" and
"input_1" to "3", with formlet-process, then it returns:
(list 10 3)
which is the second part of the formlet syntax, where month has been replaced with the
integer represented by the "input_0" and day has been replaced with the
integer represented by the "input_1".
The real power of formlet is that they can be embedded within one another. For instance, suppose we want to
combine two date forms to capture a travel itinerary. The following formlet does the job:
(define travel-formlet |
(formlet |
(div |
"Name:" ,{input-string . => . name} |
(div |
"Arrive:" ,{date-formlet . => . arrive} |
"Depart:" ,{date-formlet . => . depart}) |
(list name arrive depart)))) |
(Notice that date-formlet is embedded twice.) This is rendered as:
(list |
'(div |
"Name:" |
(input ([name "input_0"])) |
(div |
"Arrive:" |
(div "Month:" (input ([name "input_1"])) |
"Day:" (input ([name "input_2"]))) |
"Depart:" |
(div "Month:" (input ([name "input_3"])) |
"Day:" (input ([name "input_4"])))))) |
Observe that formlet-display has automatically generated unique names for each input element. When we pass
bindings for these names to formlet-process, the following list is returned:
The rest of the manual gives the details of formlet usage and extension.
6.2 Syntactic Shorthand
Most users will want to use the syntactic shorthand for creating
formlets.
Constructs a
formlet with the specified
rendering and the processing
resulting in the
yields-expr expression. The
rendering form is a quasiquoted
X-expression, with two special caveats:
,{=> formlet-expr name} embeds the
formlet given by
formlet-expr; the result of this processing this formlet is
available in the
yields-expr as
name.
,{=> formlet-expr (values name ...)} embeds the
formlet given by
formlet-expr; the results of this processing this formlet is
available in the
yields-expr as
name ....
6.3 Functional Usage
The syntactic shorthand abbreviates the construction of
formlets with the following library.
These combinators may be used directly to construct low-level formlets, such as those for new INPUT element
types. Refer to
Predefined Formlets for example low-level formlets using these combinators.
A
formlet’s internal representation is a function from an initial input number
to an
X-expression forest rendering, a processing function, and the next allowable
input number.
Constructs a
formlet that has no rendering and always returns
value in
the processing stage.
Constructs a
formlet with a rendering equal to the concatenation of the renderings of
formlets
f and
g;
a processing stage that applies
g’s processing results to
f’s processing result.
Equivalent to
cross lifted to many arguments.
Constructs a
formlet with the rendering
r and the identity procedure as the processing step.
Constructs a
formlet with the rendering
(list (list* tag attrs inner-rendering)) where
inner-rendering is
the rendering of
inner and the processing stage identical to
inner.
Runs the processing stage of f on the bindings in r.
6.4 Predefined Formlets
These
formlets are the main combinators for form input.
This
formlet is rendered with
render, which is passed the input name, and results in the
extracted
binding.
This
formlet is rendered with
render, which is passed the input name, and results in all the
bindings that use the name.
This
formlet renders using an INPUT element with the TEXT type and the attributes given in the arguments.
This
formlet renders using an INPUT element with the PASSWORD type and the attributes given in the arguments.
This
formlet renders using an TEXTAREA element.
This
formlet renders using a INPUT elemen with the CHECKBOX type and the attributes given in the arguments.
This
formlet renders using an SELECT element with an OPTION for each element of the sequence. If
multiple? is
#t, then multiple options may be selected. An element is selected if
selected? returns
#t. Elements are displayed with
display.
This
formlet renders using an SELECT element with an OPTION for each element of the sequence. An element is selected if
selected? returns
#t. Elements are displayed with
display.
Converts f’s output to a boolean, if it is equal to #"on".
6.5 Utilities
A few utilities are provided for using
formlets in Web applications.
Uses
send/suspend to send
f’s rendering (wrapped in a FORM tag whose action is
the continuation URL (wrapped again by
wrapper)) to the client.
When the form is submitted, the request is passed to the
processing stage of
f.