Using forms in request handlers

Form handling is easy within request handlers.

Simple form example

This example defines a form with two form elements, and a request handler which presents a blank form and asks the user to fill it in. One of the fields looks up items from the object store. Submitted forms are validated, and re-rendered with error messages if they fail. Once filled in, the request handler does something with the data and redirects.

// Define data source
P.dataSource('person-list', 'object-lookup', [T.Person, T.Organisation]);

// Define form, using specification from a JSON file
var exampleForm = P.form("example_form", "form/example.json");

// Request handler
P.respond("GET,POST", "/do/example_form_plugin/example", [
], function(E) {
    // Blank document to use empty values in each field
    var document = {};
    // Handle the form
    var form = exampleForm.handle(document, E.request);
    if(form.complete) {
        // Do something with the data
        //  ...
        // Redirect away
        E.response.redirect("/path/to/the/item");
    } else {
        // Render the form
        E.render({
            pageTitle:'Example form',
            form: form
        });
    }
});

The specification is stored as file/form/example.json in your plugin:

{
    "specificationVersion": 0,
    "formId": "example_form",
    "formTitle": "Example form",
    "elements": [
        {
            "type": "text",
            "label": "Name",
            "required": true,
            "path": "info.name",
            "guidanceNote": "Enter the name of the item."
        },
        {
            "type": "lookup",
            "label": "Owner",
            "dataSource": "person-list",
            "path": "owner",
            "guidanceNote": "Find the name of the person or organisation which owns this item."
        }
    ]
}

The template uses the std:form() template function to display the form:

    <form method="POST"> std:form:token()
      std:form(form)
      <p> <input type="submit"> </p>
    </form>

Note that the rendered form does not include the <form> or <input type="submit"> elements, so these need to be included in your template. This allows you greater flexibility in how you use the forms than if the entire form HTML was generated.

You can also include the HTML for a form by using deferredRenderForm() and including it in a template using render().

Using per-instance choices

If the form contains per-instance choices, you need to use slightly more verbose code so the choices are provided before the form is handled.

With a choice element specified with

    {
        "type": "choice",
        "label": "System account",
        "required": true,
        "choices": "user-list",
        "path": "user"
    }

which you want to use for a list of all users in the system, you could use a request handler like the one below. The changed code is in line 6 to 8.

P.respond("GET,POST", "/do/example_form_plugin/example", [
], function(E) {
    // Blank document to use empty values in each field
    var document = {};
    // Handle the form, providing choices before the document is updated.
    var form = exampleForm.instance(document);
    form.choices("user-list", O.group(GROUP["std:group:everyone"]).loadAllMembers());
    form.update(E.request);
    if(form.complete) {
        // Do something with the data
        //  ...
        // Redirect away
        E.response.redirect("/path/to/the/item");
    } else {
        // Render the form
        E.render({
            pageTitle:'Example form',
            form: form.renderForm()
        });
    }
});