Custom forms

If you're building forms that are part of an application, you will generally be setting up forms manually and not using the Publisher forms. To do this, you need to set up formdefinition files. For a simple site, you can just set up one global formdefinition. In your siteprofile:

<apply>
  <to type="all" />
  <formdefinitions path="[path].formdef.xml" />
</apply>

(if you need to use more than one formdefinition file, give it a name= attribute of the form module:name and access it using OpenForm("formdefname#formname"))

In this fields file:

<formdefinitions xmlns="http://www.webhare.net/xmlns/publisher/forms" gid="site">
  <form name="mysubmission" gid='.mysubmissionform'
        library="mod::mymodule/webdesigns/mydesign/libs/forms.whlib"
        objectname="MySubmissionForm"
        jshandler="mymodule:mysubmissionform">
    <page>
      ....
    </page>
  </form>
</formdefinitions>

The objectname refers to a HareScript object handling this form. The jshandler refers to an optional JavaScript handler

Create a bit of HTML/Witty to render it:

[component myform]
  <form id="myformid" class="wh-form wh-styledinput" [form.formattributes]>
    [form.formprologue]
    <div class="wh-form__page">
      [form.field1.render]
      [form.field2.render]
    </div>
    <div class="wh-form__page" data-wh-form-pagerole="thankyou" role="status">
      Thank you for filling in this form
    </div>
    [form.formrendernav]
  </form>
[/component]

You can use tags such as [form.formrender] and [form.formallpages] to simplify the rendering above. See the 'witty fields' below.

Create the form handler

PUBLIC STATIC OBJECTTYPE MySubmissionForm EXTEND FormBase
<
  RECORD FUNCTION Submit(RECORD extradata)
  {
    OBJECT work := this->BeginWork();

    //Process the form.

    work->Finish();
    RETURN DEFAULT RECORD;
  }
>;

And to put it all together, get the witty data the form needs and render it:

RECORD data := [ form := this->context->GetWittyDataForForm("<formname>")
               ];
EmbedWittyComponent("myform", data);

(and to access the form, use this->context->OpenForm("<formname>")

Custom buttons

Buttons are normally rendered using [form.formrendernav]. If you want to customize these buttons and using CSS/JS isn't sufficient, you can render them manually. Make sure they conform minimally to the following structure to mark these buttons as having a submit/prev/next effect.

  <!-- Previous and next buttons require a data-wh-form-action -->
  <button/input type="button" data-wh-form-action="previous" ...>
  <button/input type="button" data-wh-form-action="next" ...>

  <!-- Submit buttons must be explicitly marked with type=submit -->
  <button/input type="submit" ...>

The wh-form__button--previous, --next and --submit classes are used to hide these buttons when their respective actions are unavailable, but you can handle that manually by looking for the wh-form--allowprevious, wh-form--allownext and wh-form--allowsubmit classes on the parent wh-form element

Form handler object

The form object has formcontext property in its Submit handler which contains the WebContext for the URL to which the form was submitted. This gives you immediate access to eg. the targetobject.

Processing the submission

The Submit handler in your form should handle the actual form submission. It should use BeginWork/Finish to manage a transaction and trigger errors where needed. BeginWork will validate all values (eg 'required') and log errors where needed. Finish will commit the transaction unless any error is encountered.

Errors should be reported back to the field responsible for the submission wherever possible, eg:

    work->AddErrorFor(^email, "Your email address has been banned");

But if you have to, you can cause a general rejection of the form:

    work->AddError("I don't like your submission in general");

Always remember that the user is waiting for your Submit handler to be invoked. Consider scheduling long running actions as a separate managed task if the form submission has succeeded (ie no errors).

Witty fields

The following witty fields are available when setting up the form template.

You can override the rendering of the fields of a single page by setting a fieldsrenderer= attribute on the page. This is a callback that will receive the Page object and can invoke page->RenderFields() to render the original fields.

Form storage

WebHare has a builtin form storage that is used for Publisher (webtool) forms. To use this storage in your custom form, you can use a storage handler in your Submit handler:

    RECORD options := CELL
        [ idfieldvalue := "email" //deduplicate results on email
        ];
    OBJECT work := this->BeginWork();
    STRING guid := this->StoreFormValue(extradata, options);
    work->Finish();

You can also generate your own GUID using GenerateUFS128BitId and pass it as the guid option.

To view the results for your form, you can use the results dialog by calling RunFormResultsDialog:

    OBJECT formfile := OpenWHFSObject(id);
    OBJECT formresults := OpenFormFileResults(formfile, [ formname := "<formname>" ]);
    RunFormResultsDialog(this, formresults);