CUSTOM QUESTION TYPES

You can define your own custom question types for forms. These question types can be found in the "Custom" tab in the form application when adding questions. Naturally, this tab only appears if there's at least one custom question type available.

As an example, we're creating a (disabled) textedit field that will generate a unique code.

Select a namespace, for example 'http://www.mysite.net/xmlns/forms'. In the module's moduledefinition.xml file (assuming the module is called 'mymodule'), create a form definition file, mymodule/data/formdef.xsd, and fill it with:

XML field definition

<xs:schema
  xmlns="http://www.mysite.net/xmlns/forms"
  xmlns:forms="http://www.webhare.net/xmlns/publisher/forms"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:sc="http://www.webhare.net/xmlns/system/common"
  targetNamespace="http://www.mysite.net/xmlns/forms"
  elementFormDefault="qualified"
  xml:lang="en"
  >

  <xs:import namespace="http://www.webhare.net/xmlns/system/common" schemaLocation="mod::system/data/common.xsd" />

  <xs:element name="generateid">
    <xs:complexType>
      <xs:annotation>
        <xs:appinfo>
          <forms:formcomponent
            icon="tollium:forms/textedit"
            tid="module.forms.myhandler"
            descriptiontid="module.forms.myhandlerdesc"
            editdefaults="name title hidetitle required groupclasses"
            fieldobject="mod::mymodule/formcomponents/formcomponents.whlib#GenerateID"
            />
        </xs:appinfo>
      </xs:annotation>
      <xs:attributeGroup ref="sc:FormBaseAttributes" />
    </xs:complexType>
  </xs:element>

</xs:schema>

editdefaults controls which fields are made available when editing the component. The following editdefaults are supported: "name", "title", "hidetitle", "required", "noenabled", "novisible", "groupclasses", "label", "placeholder", "prefix", "suffix", "infotext"

By default, only the 'enabled' and 'visible' condition fields will be shown as they require an explicit opt-out. Most components will choose name title hidetitle required groupclasses.

Refer to this form definition in your moduledefinition.xml by adding the following tags (if you have an existing <publisher> section, just add the <formcomponents> there):

  <publisher>
    <formcomponents namespace="http://www.mysite.net/xmlns/forms" xmlschema="data/formdef.xsd"/>
  </publisher>

Once you've setup the XSD, the field is available for use in custom forms and when you manually edit the sourcecode for a form.

To enable users to select the custom fields in the form editor, your siteprofile needs to enable the new question type(s). This can be done by adding this piece of code to your main site profile:

<apply>
  <to type="file" filetype="http://www.webhare.net/xmlns/publisher/formwebtool" />
  <allowformquestion type="http://www.mysite.net/xmlns/forms#*" />
</apply>

Component code

The fieldobject needs to point to the server-side code handling the field. All form fields must (eventually) derive from FormFieldBase, but you can consier using SelectFormFieldBase as a base class if you're building a component that is simply wrapping a <select>, or ComposedFormFieldBase as a generic base class for holding one or more dynamically created components.

This example creates a sub-textedit prefilled with a random value

LOADLIB "mod::publisher/lib/forms/components.whlib";

PUBLIC STATIC OBJECTTYPE GenerateID EXTEND ComposedFormFieldBase
< PUBLIC PROPERTY value(GetValue, SetValue);

  OBJECT idfield;

  MACRO NEW(OBJECT form, OBJECT parent, RECORD field)
  : ComposedFormFieldBase(form, parent, field)
  {
    this->idfield := this->CreateSubField("textedit", "generateid");
    this->idfield->htmltitle := this->htmltitle;
    this->idfield->required := TRUE;
    this->idfield->enabled := FALSE;
    this->SetupComposition( [ this->idfield ]);

    // for this example's purpose, generate a simple code
    this->idfield->value := ToString(Random(0, 999999));
  }

  STRING FUNCTION GetValue()
  {
    RETURN this->idfield->value;
  }

  MACRO SetValue(STRING value)
  {
    this->idfield->value := value;
  }
>;

Custom attributes

Your form can accept custom attributes by

Example of such a parser:

PUBLIC RECORD FUNCTION ParseGenerateId(RECORD fielddef, OBJECT node, RECORD parsecontext)
{
  INSERT CELL code := node->GetAttribute("code") INTO fielddef;
  RETURN fielddef;
}

You cannot define on attributes such as Tollium's onchange that callback into your Form code. It's not possible to set up these kind of attributes as forms and components don't have the separate Init and StaticInit steps that Tollium adds to allow references to the containing screen.

Configuring custom attributes

To allow custom attributes to be managed in the form editor, add an editextension to the forms:formcomponent node

For example, create a mymodule/formcomponents/formcomponents.xml file containing the following:

<screens xmlns="http://www.webhare.net/xmlns/tollium/screens">
  <tabsextension name="generateidformfield"
                 implementation="lib"
                 lib="mod::mymodule/formcomponents/formcomponents.whlib">
    <insert position="answers" where="after">
      <textedit name="code" tid="module.forms.code" width="16x" required="true" />
    </insert>
  </tabsextension>
</screens>

and add this to the form:formcomponent: editextension="mod::mymodule/formcomponents/formcomponents.xml#generateidformfield"

Acceptable insert positions are: titles, answers, presentation, validation, dependencies, advanced

Add the necessary logic:

<?wh
LOADLIB "mod::publisher/lib/forms/editor.whlib";

PUBLIC OBJECTTYPE GenerateIdFormField EXTEND FormComponentExtensionBase
<
  UPDATE PUBLIC MACRO PostInitExtension()
  {
    this->code->value := this->node->GetAttribute("code");
  }

  UPDATE PUBLIC MACRO SubmitExtension(OBJECT work)
  {
    this->node->SetAttribute("code", this->code->value);
  }
>;