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.
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 ,(=> formletname) 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 103)
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:
(div(div
(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:
In all these examples, we used the input-int and input-string formlets. Any value with the formlet contract can be used in these positions. For example, (to-string (required (text-input ))) could be used as well. The rest of the manual gives the details of formlet usage, extension, and existing formlet combinators.
syntax
( formlet rendering-xexpryields-expr)
name)
syntax
( formlet* rendering-expryields-expr)
name)
name)
value
Changed in version 1.3 of package web-server-lib: Fixed support for multiple return values.
value
Changed in version 1.3 of package web-server-lib: Fixed support for multiple return values.
procedure
( xml-forest r)→(formlet/c procedure? )
procedure
( xml r)→(formlet/c procedure? )
r:xexpr/c
procedure
( text r)→(formlet/c procedure? )
r:string?
tag:symbol?
procedure
procedure
[ #:valuevalue#:sizesize#:max-lengthmax-length#:read-only?read-only?#:attributesattrs])type:string?
procedure
#:sizesize#:max-lengthmax-length#:read-only?read-only?#:attributesattrs])
procedure
#:sizesize#:max-lengthmax-length#:read-only?read-only?#:attributesattrs])
procedure
#:rowsrows#:colscols#:attributesattrs])
procedure
[ #:attributesattrs#:checked?checked?#:displaydisplay
procedure
[ #:attributesattrs#:checked?checked?#:displaydisplay#:wrapwrap])
procedure
( submit value[#:attributesattrs])
procedure
( reset value[#:attributesattrs])
procedure
( file-upload [#:attributesattrs])
procedure
( hidden value[#:attributesattrs])
procedure
src[ #:heightheight#:longdescldesc#:usemapmap#:widthwidth
procedure
button-text[ #:disableddisabled#:valuevalue#:attributesattrs])
procedure
[ #:attributesattrs#:multiple?multiple?#:selected?selected?
procedure
[ #:attributesattrs#:selected?selected?
procedure
( to-boolean f)→(formlet/c boolean? )
value
Changed in version 1.3 of package web-server-lib: Weakened result contract to allow any number.
value
procedure
[ #:methodmethod
procedure
( embed-formlet embed/urlf)→xexpr/c
A few additional considerations apply when using formlets with stateless #lang web-server servlets.
First of all, continuations captured in your servlet cannot be serialized if they close over non-serializable data-structures. There are some generally-applicable ways to avoid having a data structure be part of the closure: for example, if you define all of your formlets as module-level variables, they will never be part of closures and will not need to be serialized. However, sometimes it can be useful to create formlets dynamically. To support this, all of the combinators documented above produce formlets that are serializable (as long as they contain only serializable values). This is not guaranteed to be true of third-party formlets.
One potential pitfall for formlets and serialization is pure . Note that (serialize (pure + )) will fail, because + is not serializable. To avoid this, you can write (pure (λ args(apply + args))) (in #lang web-server, where anonymous procedures are serializable, or using web-server/lang/serial-lambda).
Secondly, stateless #lang web-server servlets are based on different web interaction primitives than stateful servlets, so the version of send/formlet from web-server/formlets will not work. Instead, the library web-server/formlets/stateless provides the same API as web-server/formlets, but with a version of send/formlet for use in stateless servlets. (Using web-server/formlets/stateless also provides all of the bindings from web-server/formlets/lib, whereas web-server/formlets provides only some of them.) Alternatively, you can use the low-level formlet-process and formlet-display procedures directly.
Another issue concerns capturing continuations within the processing stage of a formlet. Recall that serializable continuations in #lang web-server can only be captured from within transformed contexts. The contract system is not transformed, so the contracts on this library prevent capturing continuations during the processing stage of formlets. In most cases, the best solution is simply to avoid using continuation-capturing operations during a formlet’s processing stage. Instead, have the processing stage return a value, and interact with the user based on that value in code outside of the formlet. Alternatively, you can use generally-applicable approaches for capturing continuations from untransformed contexts, such as web-server/lang/native. However, if neither of those approaches are satisfactory, the library web-server/formlets/unsafe provides the same API as web-server/formlets/stateless, but without enforcing contracts. As the name implies, using web-server/formlets/unsafe may produce inscrutable error messages and other unpleasant effects of programming without contracts: you have been warned.
Changed in version 1.3 of package web-server-lib: Added web-server/formlets/stateless and web-server/formlets/unsafe and changed combinators from web-server/formlets to produce serializable formlets.