7 Web Programming

7.5 Forms with User Input

In many applications, dynamic web pages should not only depend on the environment of the web server but also on the input provided by the client (i.e., the user contacting the server via its browser). In principle, this is possible since HTML includes also elements for user input (text fields, buttons, etc) which is sent to the web server when requesting a document. How can we access the user input in a CGI program running on the server? The whole purpose of the Common Gateway Interface is to define a method to send information to the server and from the web browser, hence the name Common Gateway. Fortunately, it is not necessary to know all the details of CGI since the library HTML.Base defines an abstraction layer to provide a comfortable access to user inputs. This abstraction layer exploits the functional and logic features of Curry and will be explained in this section.

Input elements are contained in HTML forms that are embedded in HTML pages. If a form is submitted to the web server, the contents of the input elements, e.g., the text typed into text fields, is transmitted to the web server. To refer to the contents of input elements, there is a type

data HtmlRef = ...

This type is abstract and the library HTML.Base does not export any constructor for this type. We will see later how to use such abstract HTML references.

The library HTML.Base uses such references in the definitions of the various input elements occurring in HTML forms. For instance, the element “textField” defines an HTML input element where the user can type a line of text:

textField :: HtmlRef -> String -> HtmlExp

The first argument is the reference to this input field and the second argument is the initial contents shown in the field. It should be noted that this input element has type HtmlExp rather than BaseHtml. This distinction is useful to avoid errors with input fields. Since input fields can only occur inside forms, forms contain data of type HtmlExp and are embedded in HTML pages, i.e., data of type BaseHtml. Note that the abbreviations of common HTML tags shown above (htxt, h1, h2, italic,) are actually overloaded (via type classes) so that they can be used as BaseHtml and also as HtmlExp data.

How can we use a textField if there are no constructors of type HtmlRef? The simple and may be surprising answer is: by logic variables! For instance, a form containing a string and an input field can be defined as follows:

rdForm = [htxt "Enter a string: ", textfield tref ""]
 where tref free

A HtmlRef variable serves as a reference to the corresponding input field to access the user’s input. Raw CGI requires concrete strings as references (attribute “name” of “input” tags) which is error-prone (since typos in these strings lead to run-time errors). However, the concrete strings are not important, and so the logic variables are sufficient. It is only important to use them when computing the answer to the client. For this purpose, the library HTML.Base defines an HTML environment as a mapping from HTML references to strings:

type HtmlEnv = HtmlRef -> String

An HTML environment is used to collect the input of the user when computing the response. The computation of the response is done by an HTML event handler that is attached to each button for submitting a form to the web server. For this purpose, the library HTML.Base defines the type of HTML event handlers as

type HtmlHandler = HtmlEnv -> IO HtmlPage

i.e., an event handler is called with the current HTML environment and yields an I/O action that returns a new HTML page to be sent back to the client. Thus, the library HTML.Base contains the following type definition for a button to submit forms:

button :: String -> HtmlHandler -> HtmlExp

The first argument is the text shown on the button and the second argument is the event handler called when the user clicks this submit button.

The actual event handlers can simply be defined as local functions attached to forms so that the HtmlRef variables are in scope and need not be passed. To see a simple example, we show the specification of a form where the user can enter a string and choose between two actions (reverse or duplicate the string) by two submit buttons (see Figure 7.1) [Browse Program][Download Program]:

A simple string reverse/duplication form
Figure 7.1: A simple string reverse/duplication form
rdFormContents :: [HtmlExp]
rdFormContents =
  [htxt "Enter a string: ", textfield tref "", hrule,
   button "Reverse string"   revhandler,
   button "Duplicate string" duphandler]
 where
   tref free
   revhandler env = return $ page "Answer"
     [h1 [htxt $ "Reversed input: " ++ reverse (env tref)]]
   duphandler env = return $ page "Answer"
     [h1 [htxt $ "Duplicated input: " ++ env tref ++ env tref]]

Note the simplicity of retrieving values entered into the form: since the event handlers are called with the appropriate environment containing these values (parameter “env”), they can easily access these values by applying the environment to the appropriate HTML reference, like “(env tref)”.

So far we have the definition of a form, but how can such forms be embedded in an HTML page? Note that a form has two different purposes:

  1. 1.

    A form defines the layout and input fields shown to the user.

  2. 2.

    A form defines the reaction via handlers when it is submitted.

For this purpose, the library HTML.Base distinguishes between a form definition and its actual use. For this purpose, forms must be defined as top-level entities (which will be compiled by curry2cgi). For this purpose, there is a constructor simpleFormDef (other constructors are shown later):

simpleFormDef :: [HtmlExp] -> HtmlFormDef ()

This operation wraps a form layout, possibly containing input elements and event handlers, into a form definition (the type argument of HtmlFormDef will be discussed later). For instance, we turn our form above into a form definition by

rdFormDef :: HtmlFormDef ()
rdFormDef = simpleFormDef rdFormContents

A defined form can be embedded into an HTML page by wrapping the form definition with operation

formElem :: HtmlFormDef a -> BaseHtml

Hence, the following code defines an HTML page containing our form:

rdPage :: IO HtmlPage
rdPage = return $ page "String reverse/duplicate" [formElem rdFormDef]

By defining the form together with the various handlers in one top-level entity, forms are more reliable compared to a separate definition of HTML structures and scripts to process them. The advantage of distinguishing a form definition from its actual use is that we can use a form in any HTML document, in particular, recursively in the answer computed by an event handler. For instance, a form to compute the length of an input string and showing the form again in the answer can be defined as follows [Browse Program][Download Program]:

lengthForm = simpleFormDef
  [htxt "Enter a string: ", textField ref "", button "Length"
    (\env -> return $ page "Answer"
               [h1 [htxt $ "Length: " ++ show (length (env ref))],
                hrule, formElem lengthForm])]
 where ref free
main :: IO HtmlPage
main = return $ headerPage "String length" [formElem lengthForm]