Formulator HOWTO

  Introduction

    This HOWTO is intended to give an introduction to the use of
    Formulator from the Zope Management Interface and from DTML,
    although much of this applies to use from ZPT or Python as
    well. Note that Formulator comes with online help for each tab as
    well as API help, so be sure to check that as well. This document
    will only give an overview of the possibilities and not all the
    details.

  Formulator Scope

    Formulator is a tool to create and validate web forms in Zope.
    Formulator takes care of the rendering of the fields in the form,
    as well as the validation and processing of the data that is
    submitted. Formulator's scope is limited to forms: "do web forms,
    web forms only, and web forms well." Formulator does currently not
    even take care of the precise layout of a form -- each form is
    layouted differently and thus layout is left to the developer.
    Formulator does allow for easy integration with external systems,
    however.

  Creating a Formulator Form and Fields

    It is easy to create a Formulator Form, just pick it from the add
    list and add it to a folder. I usually only have one form in a
    folder and call it 'form' to make automatic layout handling
    easier; I'll say more about the reason for this later.

    The default view of the form looks just like a folder, except that
    the only things that are addable are Formulator Fields. When you
    add a field to a form, it'll show up in the Form, just like an
    object shows up in a normal Zope Folder.
    
  Fields

    When you click on a field, you see a list of its properties in the
    field's 'Edit' screen. This is a good time to explain that
    Formulator has an extensive help system, and that if you click on
    'help' in the 'Edit' screen you'll see a list with a short
    description of what each property does.

    If you click on the 'Test' tab in the Field, you will see the
    field displayed as it would appear in the form. If you fill in
    some value in the field and click on the 'Test' button, you can
    test its validation behavior. If everything could be validated and
    processed all right, you'll see the resulting value. If it could
    not be validated however, you see an error, showing the error_key
    and error_text.

    The best way to learn about what the different fields do and how
    their properties work is to try them out. Just change some
    properties and see what happens in the Test screen. And be sure to
    look at the help.

  Other Form tabs

    The form 'Test' tab is not difficult to explain; it shows all the
    fields you have added to the form. You can test the behavior of
    the entire form here. 

    In the 'Order' part you can group fields and order them inside
    their groups. The order determines the order in which they appear
    on the 'Test' screen, and can also can be used in your own
    code. Initially there is only a single 'Default' group, but you
    can add new groups and change their names.
  
    In the 'Settings' tab you can determine the form properties.  You
    can set the form submit action and method here, which you can
    later use with the 'header()' and 'footer()' methods of the form.

  Other Field tabs

    The field 'Override' screen allows you to make the field call an
    override method (most commonly a Python Script) for a property.
    Instead of using the property value in the 'Edit' screen, the
    method with the name listed in the override tab will be called to
    retrieve a value then. The returned value must be the same as the
    one that property's field generates; for an IntegerField this is
    an integer, for instance. The titles of overridden fields will be
    displayed between square brackets ('[ ]') in the 'Edit' screen.

    In the 'Messages' screen you can set the text of the error
    messages that field can generate upon validation errors.

  On the examples in this HOWTO

    All the examples in this HOWTO are contained in the file
    'formulator_howto_examples.zexp', which you can download from the
    Formulator product page
    (http://www.zope.org/Members/faassen/formulator) and import into
    your Zope. In the examples, all the forms are called 'form'.

  Rendering a form manually with DTML ('manual' folder)

    First, I will show how to use DTML to manually layout a form. This
    takes the most work, but also allows the most flexibility. In all
    these examples I will assume the form is called 'form'.

    The form contains three fields; a StringField 'animal', a
    StringField 'color', and an IntegerField 'number'. 'index_html' is
    the DTML Method that does the manual layout::
   
      <dtml-var standard_html_header>

      <!-- show the header of the form, using 'Form action' and 
	'Form method' form settings (<form action="..." method="...">)  
	-->
      <dtml-var "form.header()">

      <!-- a simple table for layout purposes -->
      <table border="0">

      <!-- each field will be on a line by itself -->

      <tr>
      <!-- first display the title property of the animal field -->
      <td><dtml-var "form.animal.get_value('title')"></td>
      <!-- render the field -->
      <td><dtml-var "form.animal.render()"></td>
      </tr>

      <!-- the same for the color field -->
      <tr>
      <td><dtml-var "form.color.get_value('title')"></td>
      <td><dtml-var "form.color.render()"></td>
      </tr>

      <!-- and the number field -->
      <tr>
      <td><dtml-var "form.number.get_value('title')"></td>
      <td><dtml-var "form.number.render()"></td>
      </tr>

      <!-- the submit button -->
      <tr>
      <td><input type="submit" value=" OK "></td>
      </tr>

      </table>

      <!-- the form footer -->
      <dtml-var "form.footer()">

      <dtml-var standard_html_footer>

    This shows a form with the three fields. You can easily rearrange
    the layout just by changing the HTML.

  Rendering a form automatically with DTML ('automatic' folder)

    For many simple forms you don't need to do the layout yourself all
    the time. We can use Formulator and acquisition to make layout a
    lot easier. If we know each form is in a separate folder and is
    called 'form', we can place DTML method in the root of the site
    that can render any such form. In this example 'index_html' will
    do the automated rendering directly. In real-world sites you'd
    usually use another method (for instance called 'form_body') to
    render because not all folders would contain forms. In that case
    it'd be easier to put the form rendering code in another method
    (for instance called 'form_body'), which you can then call from
    your other code. Here's 'index_html'::

      <dtml-var standard_html_header>

      <!-- show the header of the form, using 'Form action' and 
	'Form method' form settings (<form action="..." method="...">)  
	-->
      <dtml-var "form.header()">

      <!-- a simple table for layout purposes -->
      <table border="0">

      <!-- get a list of all fields in the form -->
      <dtml-in "form.get_fields()">
      <!-- rename each sequence item to 'field' so they can
	   be used more easily -->
      <dtml-let field=sequence-item>

      <!-- each field will be on a line by itself -->    
      <tr>
      <!-- display the title property of this field -->
      <td><dtml-var "field.get_value('title')"></td>
      <!-- render the field -->
      <td><dtml-var "field.render()"></td>
      </tr>

      </dtml-let>
      </dtml-in>

      <!-- the submit button -->
      <tr>
      <td><input type="submit" value=" OK "></td>
      </tr>

      </table>

      <!-- the form footer -->
      <dtml-var "form.footer()">

      <dtml-var standard_html_footer>

    The nice thing about the automatic approach is that now you can
    change the Formulator form as much as you like; this code will
    always automatically display them. Even better, if you add
    subfolders with forms in them, acquisition makes those forms
    display automatically as well! If you have only simple forms on a
    site, this could be the only DTML Method you need.

  Form validation ('validation' folder)

    I will use the same 'index_html' as in the automatic form
    rendering example and the 'animal/color/number' form to
    demonstrate form validation.

    I've set the 'Form action' property of the form to 'feedback'.
    When the form is submitted it, Zope will access the 'feedback'
    DTML Method. The form data will be coming into 'feedback' in the
    'REQUEST' object (more precisely the 'REQUEST.form' object).

    The 'feedback' method should do a number of things:

      * validate all fields (tell formulator to take care of this).
  
      * handle any validation errors.

      * if there were no validation errors, do something with the
        form results.

    Here's 'feedback', with comments::

      <dtml-var standard_html_header>
      <dtml-try>
	<!-- try the validation, results should be put in
	     REQUEST (keyed under the field id) --> 
	<dtml-call "form.validate_all_to_request(REQUEST)">  
      <dtml-except FormValidationError>
	<!-- if something went wrong with any field validation,
	     a FormValidationError will be raised, which we
	     will then catch here -->
	<!-- we will display the errors here -->
	<ul>
	<dtml-in "error_value.errors">
	  <li>
	  <dtml-var "field.get_value('title')">:
	  <dtml-var error_text>
	  </li>
	</dtml-in>
	</ul>

      <dtml-else>
	<!-- if no FormValidationError was raised, we're done
	     with validation and our results will now be in
	     REQUEST (and in DTML namespace). -->

	<!-- we could do anything with them, but we'll simply
	     display them -->
	Hah, you are a <dtml-var color> <dtml-var animal> with
	<dtml-var number> legs.

      </dtml-try>

      <dtml-var standard_html_footer> 
    
    Note that often you can use acquisition with the validation page
    as well, so you can reuse most of its functionality.
  


