Forms Development


This topic explains how to create new form logic in the XperienCentral Interactive Forms component. For developers, the most important parts of the Interactive Forms component are the following:

  • Form logic (handler, router, form validator, element validator)
  • Form element (also known as a form fragment)
  • Form presentation (for the frontend and for the backend)
  • Form manager
  • Form engine
  • Incorporation of AJAX on the frontend of the forms

Each of these parts of the Interactive Forms component is described in detail in the following parts of this topic.

In This Topic



Form Logic

Form Logic is a generic term for the following four concepts in Interactive Forms: handler, router, form validator and fragment validator. The screenshot below shows where new handlers (A), routers (B) and form validators (C) that you create will appear in the Interactive Forms user interface:



Form Fragment

The parts that make up a form are called form elements. When discussing form elements in connection with the API, we refer to form elements as form fragments. Both of these terms are used interchangeably in this topic. In the GUI of the Interactive Forms component, form elements are divided into three types:

  • Elements that require input from the website visitor;
  • Elements that contain zero or more other elements;
  • Elements that don’t require input from the website visitor.

Presentation

Each part of a form has its own presentation. The presentation for Interactive Forms is handled using JSPs in the same way that the presentations in the rest of XperienCentral are handled.

Presentation Scopes

There are many scopes relevant for the Interactive Forms presentation. Presentations with the scope FormStep are available as a presentation for each form step. Apart from this scope, there is also a scope for each sort of form fragment. For example, the form fragment with the name "Text" has the scope FormFragmentTextInput. All default presentations can be found in the plugin nl.gx.forms.wmpformpresentation.

Just like with elements on a page, if a form element has only one presentation with the scope belonging to that element, there will be no presentation option for that form element available to the editor. If a form element has more than one presentation within its scope, then the property "Presentation" appears at the top of the advanced properties of that form element. For example, by default the Text form element has two presentations and therefore the Presentation property has two options.

Frontend and Backend Presentation

The presentation in the form’s JSP(s) is used for rendering it in the frontend. For the backend, the "iafpanel" channel is used, which must be defined in the descriptor.xml file that accompagnies the JSP.  For example, for a text input fragment presentation, this would look similar to the following:


<channel>
    <name>iafpanel</name>
    <presentation>FormFragmentTextInput</presentation>
</channel>


When the HTML output of a form element in the Interactive Forms component as shown in XperienCentral must be identical to the HTML output on the website, the value of the presentation tag should be the same as the name of the presentation. This is what makes the WYSIWYG of the Interactive Forms component possible. Otherwise, the presentation tag should be set to the name of IAF's official presentation for the corresponding scope.

Form Manager and Form Engine

The Form Manager is responsible for handling the Interactive Forms objects, that is, retrieving, storing and/or deleting objects (CRUD operations). The Form Engine deals with form operations such as creating form sessions and handling the form logic for a specific form.

AJAX on the Frontend

It is common to use AJAX in forms on the frontend of a website. For example, when a website visitor enters his or her postal code and house number into a field, you can automatically complete the street name and city. This is done without having to submit the complete form. AJAX performs this in the following ways:

  • Using JavaScript in the browser, a call is made to the server using just the postal code and the house number;
  • The server returns an XML string containing the city and street name;
  • The JavaScript in the browser processes the XML response and fills in the city and street name fields with the returned information.

It’s possible to use AJAX on the frontend for forms. This process is briefly explained in Clientside routing: AJAX development using the JSON interface.


Back to top



Architectural Overview

Entities

All of the concepts discussed in this part can be translated into entities stored in the JCR and interfaces in the API. XperienCentral editors use the Interactive Forms maintenance panel to manage these entities.

Form

The form is the top level entity within Interactive Forms. Each form can have multiple versions, which are basically version-controlled "wizards" in the sense that they can consist of multiple form steps. One of those versions is active while the other versions are in other states such as "development" or "inactive". The latter two have the same functional effect: each form version can have multiple form steps which are to be displayed and handled one at a time by the form element. The form element is a normal XperienCentral element that can be placed on a page. Optionally, a specific form step can be selected within the element. By using multiple form elements, you can spread the different steps of a form over multiple pages. Forms can also be ordered in categories.

Form Step

A form step is a presentation canvas for the website frontend. The form step is a container that contains one or more form fragments that can be added, removed and/or ordered in the user interface. A form step entity belongs to one specific form entity. Moreover, a form step is a form logic container, see Form Logic Container.

Form Section

A form section is conceptually the same as a form step, with the exception that they can be reused. Form sections can have multiple versions, just like forms. Form sections are placed on a form step. They can be used on multiple steps, versions and forms. Form sections are also referred to as sub forms. Form sections can also be ordered in categories.

Form Fragment (Container)

All visible components of a form step are form fragments. These can range from a simple text input to complex composite elements, for example a form section form fragment or an address fragment consisting of multiple fields. Complex form fragments can contain their own form logic. Form fragments typically have several default properties like the title, the identifier (their technical name), a precondition, a prefilling value and set of validators. Everything that can contain a form fragment is referred to as a FormFragmentContainer. Examples of a container are a form step, a form fragment that renders a form section, a composite form fragment or a simple design fragment like a column fragment. All form fragments are of the component type FormFragmentComponentType and can be generated by a plugin.

Form Logic Container

Apart from form fragments, form steps also contain form logic that can occur in multiple places, also known as form logicContainers which are a special type of FormFragmentContainer which always contain form fragments. Examples of such form logicContainers are the form step, the form section and the form fragment that displays a form step. Form logic is typically a list of form logic components that are executed one after the other. However, they can also be grouped in a tree-like way using, for example, the conditional form logic component. These form logic components come in several varieties: handlers, validators, routers and form validators. Except for the validator, these components are basically identical. The difference is that validators cannot be assigned to a form logic container, but only directly to the form fragment they should validate.

Form logic can be executed both before rendering a form step and after. The former is referred to as prehandling and the latter is referred to as posthandling. Before rendering a form step, its tree of prehandling FormFragments, FormFragmentContainers and form logicContainers is stepped through and the form logic components encountered are executed first. All form logic can return a routing result; if one is returned, execution stops. In the case of prehandling, by default the result has no effect on the execution process.

When a form step is submitted, the posthandling process is followed. In this case, the routing result is handled and the user is redirected accordingly. Form logic can be extended by installing a formlogicProvider plugin. Such form logicComponents can be written in either Java, JavaScript or a combination of both. What actually happens when a form is rendered or submitted is much more complex (see The Form Engine for more details).

Language Labels

Interactive Forms has its own solution for providing language labels using language label containers. Most entities discussed above have their own set of language label containers. Within these containers, language-specific properties of these entities are stored. This typically involves the title and additional text shown to the user. Typically, there is a language label container for each supported locale that corresponds to the "metatagvalue" of the XperienCentral language (nl_NL or en_US).

The Interactive Forms API

The Interactive Forms API defines all interfaces that are used to communicate with the Interactive Forms component. Plugin developers only use these contracts for forms related services, to perform CRUD operations on forms, to submit forms and handle form operations. A complete overview of the packages can be found in the XperienCentral Javadoc. The following packages are the most used in the plugin development process:

nl.gx.forms.wmpformapi.api.form

An extension of the basic form interfaces in nl.gx.forms.wmpformapi.api. Some interfaces extend the XperienCentral wrapper/presentable interface that allows the displaying of objects using the WM render mechanism. The basic form interfaces (nl.gx.forms.wmpformapi.api) only contains getters — these form interfaces also contains create, delete and update (setter) methods.

nl.gx.forms.wmpformapi.api.logic

Contains interfaces that define the form logic components that can be assigned to a form step. The typical form logic components are form validators, form fragments validators, form routers, form handlers and combinations of these through the use of conditional form logic component (the if-then-else component).

nl.gx.forms.wmpformapi.api.manager

Form entities are managed by a shell around the XperienCentral EntityManager, namely the FormManager (wmpformmanager). The FormManager is designed to facilitate form maintenance actions like retrieving, adding, deleting forms or subforms (CRUD operations). Moreover, the FormManager provides form-related information like categories, form validators, handlers and routers that are present in the XperienCentral platform. The FormManager is the interface for the FormManagerService. This service can be used by defining a service dependency in your plugin. This service is also used as the starting point for CRUD operations on forms, form steps, form fragments, form logic, and all other related objects in your plugin.

nl.gx.forms.wmpformapi.engine

The Interactive Forms component exposes three service interfaces, one of which is in the engine sub package of the forms API: the form engine service (FormEngineService). The FormEngineService enables the handling of forms, manages the form engine session and executes form logic components. This service is typically used in the form handler servlet that handles incoming form submits and the form element that handles the rendering of the form.

nl.gx.forms.wmpformapi.plugin.formfragmenttype

This package provides interfaces that enable plugin developers to create their own form fragments. A form fragment uses the FormFragmentComponent to become a component, and is therefore able to be managed by the Component Manager. The FormFragmentComponent interface ensures a unified way of creating form fragment instances through the createInstance() method. The FormFragmentComponentType interface introduces the component type that identifies the class of form fragment components. The FormFragmentComponentDefinition defines the relationship between the component, component type, form fragment implementation class and more.

nl.gx.forms.wmpformapi.plugin.formlogicProvidertype

This package provides interfaces that enable plugin developers to write their own form logic. The formlogicProviderComponent is the XperienCentral component that provides form logic (routers, handlers and validators). Form logic typically implements this interface. The formlogicProviderComponentType interface introduces the component type that identifies the class of form logic components. The formlogicProviderComponentDefinition defines the relation between the component, component type, form logic implementation class, and so forth. All form logic components typically implement the formlogicProviderService, which allows them to be executed by the form engine.

Form Fragment Java Interfaces

The following table shows the Java interfaces used by the Interactive Forms form fragments.


Form Fragment NameJava Interface
Textnl.gx.forms.wmpformfragments.api.FormFragmentTextInput
Numbernl.gx.forms.wmpformfragments.api.FormFragmentNumberInput
Datenl.gx.forms.wmpformfragments.api.FormFragmentDateInput
Passwordnl.gx.forms.wmpformfragments.api.FormFragmentPasswordInput
Textareanl.gx.forms.wmpformfragments.api.FormFragmentTextArea
Radionl.gx.forms.wmpformfragments.api.FormFragmentRadioList
Checkboxnl.gx.forms.wmpformfragments.api.FormFragmentCheckboxList
Dropdownnl.gx.forms.wmpformfragments.api.FormFragmentDropDownList
Uploadnl.gx.forms.wmpformfragments.api.FormFragmentFileUpload
Emailnl.gx.forms.wmpformfragments.api.FormFragmentEmailInput
Columnsnl.gx.forms.wmpformfragments.api.FormFragmentColumns
Sectionnl.gx.forms.wmpformfragments.api.FormFragmentSection
Form Sectionnl.gx.forms.wmpformfragments.api.form.FormFragmentFormSection
Repeatnl.gx.forms.wmpformfragments.api.form.FormFragmentRepeat
Next Buttonnl.gx.forms.wmpformfragments.api.FormFragmentButton
Back Buttonnl.gx.forms.wmpformfragments.api.FormFragmentBackButton
Paragraphnl.gx.forms.wmpformfragments.api.FormFragmentParagraph
Overviewnl.gx.forms.wmpformfragments.api.FormFragmentOverview

The Clientside JavaScript Framework

As soon as a form is rendered on the website, the entities discussed in the previously in this topic come in to play. The rendering of a form is handled by the FormElement object. Rendering involves (among other things) retrieving the form, form fragments and form logic. The output of the rendering process is twofold: HTML based on the presentations coupled with the form and from fragments and JavaScript that is retrieved from form fragment preconditions and validations, that is, the clientside JavaScript framework.

Preconditions

The Interactive Forms component allows editors to define JavaScript preconditions with form fragments. Preconditions (Boolean logic expressions mostly based on identifiers of other fragments) define whether a form field is visible or hidden. These preconditions are handled on the clientside. Initially, a start state is calculated. Subsequent fragment actions are based on change events.

The standard JavaScript library of the Interactive Forms component (validation.js) ensures that all form fields are bound to a change event. When a change event is thrown, the JavaScript logic will show (or hide) other form fields according to their identifier match. The repeater fragment (FormFragmentRepeat) is a special case: JavaScript preconditions are generated automatically without editor interference. The actual JavaScript functions are rendered by a presentation JSP named showFormElementClienSideFramework.jspf which is indirectly called from the Formelement presentation using the code sample shown below. First, the showFormelement.jspf retrieves the showFormElementClientsideFrameworkCall.jspf:


<wm:link var="clientsideFrameworkLink" presentationName="showFormElementClientsideFrameworkCall" elementId="${element.id}"wmstepid="${param.wmstepid}"/>
<script type="text/JavaScript" src="${clientsideFrameworkLink.url}"></script>



The scope in the descriptor file of the showFormelement.jspf presentation is FormsElement, not FormElement.


Next, the showFormElementClientsideFrameworkCall.jspf calls the showFormElementClienSideFramework.jspf. The latter jspf retrieves a significant part of the JavaScript code by executing a getClientSideFrameWork() method call from the FormElement.java class using the code "${element.clientSideFrameWork}".


Validations

Validators are assigned to form fragments. Besides the default validators, editors can choose one or more validators for each form fragment. When the form (and thus form fragment on that form) is rendered, the JavaScript functions of these validators will be added to the clientside JavaScript framework. In order to accomplish this, the getJSFunction() method in the form logicComponentDefinition class is used.

The standard JavaScript library of the Interactive Forms component (validation.js) ensures that when a form is submitted the validation JavaScript functions will be executed. The submit is bound through the wmpform class of the form HTML tag (<form ... class="wmpform" method="post" enctype="multipart/form-data">) using the following JavaScript code $(".wmpform").submit(function(event) {...}. If a JavaScript validator fails, the submission of the form to the server will be cancelled.

Precondition JavaScript expressions are defined by the editor in the user interface. However, validator JavaScript functions are fixed to the logic component entity. Because there is no maintenance interface for these components in the Interactive Forms component, these functions should be shipped with the plugin. If a developer wants to add validator logic components to the XperienCentral framework, the JavaScript involved has to be known and referenced in the component definition in the Activator. This must also be done for the language labels that are mapped to the validation.


If the current clientside JavaScript framework architecture is not suited for a specific website implementation, the form element structure can be re-implemented. This procedure is not explained in this document however a discussion on how to extend the JavaScript framework using AJAX calls to the FormHandlerServlet for both form handling and validation appears in Clientside routing: AJAX development using the JSON interface.


Back to top



Generate Your Own Plugin Using the Interactive Forms Archetypes

The Interactive Forms component is as an extensible framework. In order to generate the source for the Interactive forms plugins, follow the steps below. In the code samples below, settings.xml refers to the XML settings file that normally resides in the root of your XperienCentral installation. Be sure that you refer to the correct path where settings.xml resides on your system.

  • Execute the following command to generate the helloworldfragment plugin:

    mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=nl.gx.forms.wmpformarchetypes -DarchetypeArtifactId=wmpformfragment-archetype -DarchetypeVersion=10.10.0 -DgroupId=com.gxwebmanager.helloworld -DartifactId=helloworld -Dclassprefix=HelloWorld -s <my_path_to_settings.xml>\settings.xml

  • Execute the following command to generate the helloworldlogicprovider plugin:

    mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=nl.gx.forms.wmpformarchetypes -DarchetypeArtifactId=wmpformlogicprovider-archetype -DarchetypeVersion=10.10.0 -DgroupId=com.gx.webmanager.helloworld -DartifactId=helloworld -Dclassprefix=HelloWorld -s <my_path_to_settings.xml>\settings.xml


The helloworldformfragment Plugin

The fragment archetype generates the following files.


File
Description
Activator.java

The bundle activator

CustomFragment.java

The interface representing the custom fragment

CustomFragmentController.java

The controller of the fragment component

CustomFragmentFBO.java

The form backing object (FBO) of the fragment component

CustomFragmentImpl.java

The component and fragment implementation (Business Object)

editCustomFragment.jspf

The JSP that renders the properties of the fragment in the right column of the maintenance panel

messages_en_US.properties

The US English language file

messages_nl_NL.properties

The Dutch language file

showCustomFragment.xml

The Descriptor of the JSP rendering the fragment on the

showCustomFragment.jspf

The JSP that renders the fragment in both the Edit environment and the frontend of the website environment

customFragment.png

The icon associated with the fragment that appears in the Insert menu


The usage of the presentation JSPs is different than JSPs for custom elements because it is used in both the frontend and backend.


Back to top



Developing a New Form Fragment Plugin

Now that the custom fragment plugin has been created, it can be modified to fit your design. If you want to try your plugin, build and deploy it to the XperienCentral platform.

  1. Open a Command prompt.
  2. Navigate to the folder where your plugin is located.
  3. Execute the following command. Be sure that you specify the correct path to your settings.xml file.

    mvn -s ..\..\XperienCentral9\settings.xml clean package

  4. Copy the file helloworldelement-1.0.0.jar from the target folder to the <xperiencentral-root>\work\deploy folder.

The fragment is now available in XperienCentral. The custom fragment has to be activated first (this can be done by navigating to Maintain> Interactive Forms> Settings in the Interactive Forms user interface. After activation, the element appears in the Insert column.


Modifying the Presentation of the Form Fragment

In the component definition in the bundle Activator, the scope of the custom fragment is defined. If you want to add extra presentations to a fragment, the descriptor file belonging to the JSP must contain a scope tag that points to the correct scope. The descriptor file and JSP file can be saved in the same folder as the showCustomFragment.xml file and showCustomFragment.jspf file. The JSP is also used for rendering the fragment in the maintenance panel, therefore check the JSP to ensure that it is suitable for both environments.

Adding a Property Field to the Form Fragment

To add a property field to the fragment, perform the following steps:

  1. Add the property to the Business object (CustomFragmentImpl.java).
  2. Add the property to the form backing object (CustomFragmentFBO.java).
  3. Add a label to the language files in order to translate your property in the supported languages.
  4. Add the field to the properties JSP (editCustomFragment.jspf).
  5. Add the field to the show/preview JSP (showCustomFragment.jspf).

These steps are identical to the steps that are described in Adding an Input Field to a Component.

Adding a default validation to the form fragment

The implementation class (for example, CustomFragmentImpl) must contain the method getDefaultValidators(). This method returns the validator(s) that you want to use by default. If the fragment is added to the form step in the Interactive Forms interface, this set of validators will be assigned. The validators that are retrieved by this method have to be present in the framework. Create Your Own Form Logic Components explains how to create a fragment validator. Suppose a fragment validator is present in the bundle activator:


formlogicProviderComponentDefinitionImpl def = new formlogicProviderComponentDefinitionImpl(false, formlogicProviderComponentDefinition.FORM_LOGIC_PROVIDER_TYPE_FRAGMENTVALIDATOR, pluginConstants.MY_VALIDATOR_IDENTIFIER);


This validator can be retrieved using the same identifier:


FormManager formDAO = (FormManager) FrameworkFactory.getInstance().getFramework().getService(FormManager.class.getName());
// Add my validator 
FragmentValidatorDefinition myValidatorDef =formDAO.getFragmentValidatorDefinitionByIdentifier(com.gxXperienCentral.helloworld.pluginConstants.MY_VALIDATOR_IDENTIFIER);
if (numberDef !=null) {
	result.add(new SimpleFragmentValidator(this, myValidatorDef));
}                   


Back to top



Create your Own Form Logic Components

Form Logic is a generic term for handlers, routers, form validators and fragment validators. A form logic component implements both the formlogicProvidercomponent interface and the formlogicProviderService interface. Usage of these interfaces requires the implementation of the run() method.

The return type of this method is a RoutingResult. This result can be either a RoutingResult or null.


class implements formlogicProvidercomponent,formlogicProviderService {
	public RoutingResult run(FormScope scope, Map<String, Object> parameters,Map<String, Object> languageLabels) {...}
}


If null is returned, there is no routing result, therefore the next logic component in the queue will be handled. If a RoutingResult is returned, execution within the handler queue is stopped and the routing result is handled. Because the formlogicProvidercomponent class is the action class of the logic component, the formlogicProviderComponentDefinition object will register the form logic component. This definition is added to the bundle Activator of a plugin.


Only one instance of the action class will be present within the XperienCentral framework. Therefore, this class should not keep track on any form specific session or user information, since it’s used by multiple forms. Instead, use the FormScope which is present as a parameter of the run() method.


When a form logic (provider) component (definition) is added to the XperienCentral framework (by deploying a plugin), a form logicComponentDefinition object will be registered. In other words, the form logicComponentDefinition object is the entity reference of the form logic component. The method getformlogicProviderComponent() returns the form logic provider component of this entity.

The subinterfaces of form logicComponentDefinition are:

  • ConditionalComponentDefinition
  • FormHandlerDefinition
  • FormRouterDefinition
  • FormValidatorDefinition
  • FragmentValidatorDefinition

The Interactive Forms component uses these subinterfaces according to the formlogicProviderType that is assigned to the formlogicProviderComponentDefinition in the activator class. Registering a logic component in the bundle Activator is similar for all form logic component types. The following code sample shows how to define a fragment validator:


private formlogicProviderComponentDefinition getBankAccountValidatorDefinition() {  formlogicProviderComponentDefinitionImpl def = new formlogicProviderComponentDefinitionImpl(false, formlogicProviderComponentDefinition.		FORM_LOGIC_PROVIDER_TYPE_FRAGMENTVALIDAT OR, pluginConstants.BANKACCOUNT_VALIDATOR_IDENTIFIER);

	def.setId(pluginConstants.BANKACCOUNT_VALIDATOR_COMPONENT_ID);
	def.setName(pluginConstants.BANKACCOUNT_VALIDATOR_COMPONENT_NAME);
	def.setDescription(pluginConstants.BANKACCOUNT_VALIDATOR_COMPONENT_DESCR);
	def.setJsFilename("bankaccount_validator.js");
	def.setTypeId(formlogicProviderComponentType.class.getName());
	def.setImplementationClassName(Simpleform logicComponent.class.getName());
	def.setProperties(new Hashtable<String, String>());
	def.addRequiredParameter(ParameterDefinition.PARAMETER_TYPE_FORMFRAGMENT_IDENTIFIER, nl.gx.forms.wmpformapi.pluginConstants.VALIDATOR_FRAGMENT_PARAMETER, nl.gx.forms.wmpformapi.pluginConstants.VALIDATOR_FRAGMENT_PARAMETER);
	def.setAllowedToRunClientside(true);
	def.addLanguageLabelMapping("INVALID_BANKACCOUNT", "invalid_bankaccount");
	def.setTitleLanguageLabel("wmpformlogicProvidersnl.validator.bankaccountvalidator.title");

return def;
}


The constructor of the component definition requires a formlogicProviderComponentDefinition type as a (second) parameter. The formlogicProviderComponentDefinition defines four types:

  • FORM_LOGIC_PROVIDER_TYPE_FRAGMENTVALIDATOR
  • FORM_LOGIC_PROVIDER_TYPE_FORMVALIDATOR
  • FORM_LOGIC_PROVIDER_TYPE_ROUTER
  • FORM_LOGIC_PROVIDER_TYPE_HANDLER

Depending on the role of the form logic component, one of these types should be used. The last parameter is the (internal) identifier of the component. The first parameter connects to the ComponentDefinition class of the XperienCentral framework and identifies the associated component as a component requiring a license.

setJsFilename

setJsFilename Indicates which JavaScript file is the base for the JavaScript that will be used for the action of the form logic component. If the form logic component doesn't require JavaScript logic, this setter can be omitted. The function that is implemented in the .js file should have the same name as the filename. For example: If the file is called bankaccount_validator.js, the implemented JavaScript function should be function bankaccount_validator(parameters, languageLabels).

setImplementationClassName

The implementation class of this component is the Simpleform logicComponent class. This class implements the formlogicProviderComponent class and has been used as a base or dummy form logic provider component. It is possible to use such a (empty) class in your plugin as the implementation class for a form logic provider that has only a JavaScript implementation. A dummy class should resemble the following:


package nl.gx.forms.yourformlogicProvidertype.impl;

import nl.gx.forms.wmpformapi.plugin.formlogicProvidertype.formlogicProviderComponent;
import nl.gx.XperienCentral.plugin.foundation.ComponentBase;

public class Simpleform logicComponent extends ComponentBase implements formlogicProviderComponent {
}



This class doesn't implement the formlogicProviderService interface. If no Java logic action (for example, validation action) is required, the formlogicProviderService doesn't have to be implemented.

addLanguageLabelMapping

The addLanguageLabelMapping() method requires 2 parameters. The first parameter is the identifier that uses the Java/JavaScript code to retrieve the value if this label, for example, the language label can be retrieved from the JavaScript using the code "languageLabels["INVALID_BANKACCOUNT"]". The second parameter is the identifier as it appears in the language_labels_xx.properties files (where xx is the locale value). These properties files have to be shipped with the plugin.

addRequiredParameter / addOptionalParameter

The addRequiredParameter() method and addOptionalParameter() method require 3 parameters. The first parameter refers to the parameter type. These types are:


File
Description

PARAMETER_TYPE_BOOLEAN

Indicates a parameter is of type Boolean.

PARAMETER_TYPE_FORMFRAGMENT_IDENTIFIER

Indicates a parameter is of type FormFragment Identifier.

PARAMETER_TYPE_FORMFRAGMENT_VALUE

Indicates a parameter is of type FormFragment Value.

PARAMETER_TYPE_LANGUAGE_LABEL

Indicates a parameter is of type LanguageLabel.

PARAMETER_TYPE_PAGE

Indicates a parameter is of type Page.

PARAMETER_TYPE_STEP

Indicates a parameter if of type FormStep.

PARAMETER_TYPE_STRING

Indicates a parameter is of type String.

PARAMETER_TYPE_TEXTAREA

Indicates a parameter is of type Textarea.


The second parameter refers to the name/identifier of this parameter. This identifier can be used as a reference in the run() method of your formlogicProviderComponent class. In Form Validator the retrieval parameters values are described. The third parameter refers to the language label that will be used in the user interface. (It should be present in the language_labels_xx.properties file.) For a fragment validator, one specific parameter has to be defined. It should be a required parameter of the PARAMETER_TYPE_FORMFRAGMENT_IDENTIFIER type. For example:


def.addRequiredParameter(ParameterDefinition.PARAMETER_TYPE_FORMFRAGMENT_IDENTIFIER, nl.gx.forms.wmpformapi.pluginConstants.VALIDATOR_FRAGMENT_PARAMETER, nl.gx.forms.wmpformapi.pluginConstants.VALIDATOR_FRAGMENT_PARAMETER);            
}


This parameter allows the Interactive Forms component to assign a fragment validator to a form fragment. Next to this parameter, extra parameters can be defined.

setAllowedToRunClientside

setAllowedToRunclientside determines whether the form logic (that is, the fragment validator) can run on the client side. This setting is merely for security purposes, for example when the JavaScript source code of a form logic component should not be exposed because it contains a business secret.


More on the Fragment Validator

Creating a simple (that is, JavaScript only) fragment validator definition is fairly straightforward. First, add a componentDefinition to your bundle Activator. Next, implement a JavaScript validator function. Finally, implement a dummy formlogicProviderComponent class and, after deploying the plugin, the validator is ready to use.

As mentioned in the Architectural Overview, the validation JavaScript function(s) of a fragment will be retrieved by the JavaScript clientside framework by accessing the connected validators. These fragment validators can be both default validators and validators assigned by the editor. The validation is performed on the clientside (with JavaScript) when the form is submitted. At this point, two questions could arise: In what way is validation dealt with on the server side, and how can a fragment validator be assigned as a default validator to a specific fragment? Let's address the first question first. When the form is posted to the webserver, either a JavaScript or Java validation can be executed. The FormEngineService will handle the validation of a fragment:


private void validateFormFragment(BasicFormFragment fragment, FormScope scope) throws FormManagerException {

	// Only continue if the fragment precondition was true!
    ...       
	if (conditionResult) {
		if (fragment instanceof BasicFormInputFragment) {
        	...
			BasicFormInputFragment ssf = (BasicFormInputFragment) fragment;
	        ...

			// getDefaultValidators() returns the set of validator instances that are always present for fields of the implemented type.

			List<Basicform logicComponent> defaultValidators = ssf.getDefaultValidators();
				if (defaultValidators != null) {
					for (Basicform logicComponent validator : defaultValidators) {
						executeform logicComponent(validator, scope, fragment);
						}
				}
				...                
				//getCustomValidators() returns the set of validator instances that were configured by the user.

				List<Basicform logicComponent> customValidators = ssf.getCustomValidators();

				if (customValidators != null) {
					for (Basicform logicComponent validator : customValidators) {
						executeform logicComponent(validator, scope, fragment);
					}
				}
			}
		}


In the code example above, the validateFormFragment() method checks to see whether the precondition for the fragment is met. If so, it retrieves the validators that are assigned to the form fragment. The executeform logicComponent() method that is called for a set of validators will retrieve and execute the validation action. This action could be either based on JavaScript or Java code. First, the JavaScript logic will be retrieved from the form logicComponentDefinition. If no JavaScript is defined, the formlogicProviderComponent object is retrieved and the run() method will be called. However, this is only done when the formlogicProviderComponent is an instance of the formlogicProviderService, therefore be sure that custom-defined formlogicProviderComponent implements the formlogicProviderService interface.

If neither a JavaScript source nor a Java implementation is available, the FormengineService will log the (severe) message: No implementation found for form logic component. Additionally, a (custom) FormFagment class should (indirectly) implement the BasicFormInputFragment interface if you want to perform server side validation. This interface requires the implementation of the method getDefaultValidators(). This particular method (again) could bring up the question, how can default Fragment validators be assigned to fragments?

If you implement the getDefaultValidators() method, you determine yourself which validators are assigned by default. The HelloWorld plugin constructed from the fragment archetype uses a hardcoded mechanism to assign the default validators. In the getDefaultValidators() method of the form fragment, a validator is directly retrieved from the Interactive Forms component using the FormManager Service which uses the identifier as a reference.

This identifier is already introduced in the component definition of the validator in the bundle Activator:


formlogicProviderComponentDefinitionImpl def =new formlogicProviderComponentDefinitionImpl(false, formlogicProviderComponentDefinition.FORM_LOGIC_PROVIDER_TYPE_FRAGMENTVALIDATOR,pluginConstants.HELLOWORLD_VALIDATOR_IDENTIFIER);


Thus, the same identifier allows you to retrieve this specific validator from the FormFragment class:


FragmentValidatorDefinition myDefaultDef=myFormManager.getFragmentValidatorDefinitionByIdentifier(pluginConstants.HELLOWORLD_VALIDATOR_IDENTIFIER);


Form Validator

Unlike the Fragment Validator, which points to one specific fragment, a form validator can validate objects more freely. Editors assign a form validator to form steps. Setting the properties of the form validator is done on the canvas where the logic entities of a form step are visualized (the Handlers and Routers panel).  In order to specify which objects have to be validated, the form validator must have one or more parameters.

To include a Form Validator in your plugin start with adding the definition type FORM_LOGIC_PROVIDER_TYPE_FORMVALIDATOR in your bundle Activator. For example:


formlogicProviderComponentDefinitionImpl def =new formlogicProviderComponentDefinitionImpl(false, formlogicProviderComponentDefinition.FORM_LOGIC_PROVIDER_TYPE_FORMVALIDATOR,pluginConstants.HELLOWORLD_MYFORMVALIDATOR_IDENTIFIER);
  
def.setId(pluginConstants.DATEDAYRANGE_VALIDATOR_COMPONENT_ID);
def.setName(pluginConstants.DATEDAYRANGE_VALIDATOR_COMPONENT_ID);
def.setDescription(pluginConstants.DATEDAYRANGE_VALIDATOR_COMPONENT_DESCRIPTION);
def.setTypeId(formlogicProviderComponentType.class.getName());
def.setImplementationClassName(Simpleform logicComponent.class.getName());
def.setJsFilename("helloworld_formvalidator.js");
def.setProperties(new Hashtable<String,String>());
def.addRequiredParameter(ParameterDefinition.PARAMETER_TYPE_FORMFRAGMENT_IDENTIFIER "anyfragment","helloworldformvalidator.anyfragment");
def.addOptionalParameter(ParameterDefinition.PARAMETER_TYPE_STRING,"anyinteger","helloworldformvalidator.anyinteger");
def.setAllowedToRunClientside(false);        
def.addLanguageLabelMapping("ILLEGAL_VALUE","helloworldformvalidator.illegal_value");
def.addLanguageLabelMapping("VALUE_TO_BIG","helloworldformvalidator.fragment_value_max_exceeded");        
def.setTitleLanguageLabel("helloworldformvalidator.title");


Given the definition above, the Interactive Forms component expects a JavaScript implementation of the validation action. Since the library file is named helloworld_formvalidator.js, the JavaScript function that must be implemented must be named helloworld_formvalidator().

The following code example shows a way in which the parameters can be retrieved:


function helloworld_formvalidator(parameters, languageLabels) {
	var myValue = this[parameters.anyfragment].value;
	var nrDays = parseInt(parameters.anyinteger);

	if (myValue != undefined && myValue != "") {
		
		error = false;

		if (!myvalue.match(/[0-9]+\.?[0-9]*/)) {
			this[parameters.anyfragment].errors["ILLEGAL_VALUE"] = languageLabels["ILLEGAL_VALUE"];
			error = true;
		}   
		if (!error) {
			// now check the value
			if (nrDays != 'NaN') {     
				
				if (nrDays >= myValue) {
					this[parameters.anyfragment].errors["VALUE_TO_BIG"]= languageLabels["VALUE_TO_BIG"];                     
				} 
			}   
		}
	}


The code example shows that parameters are retrieved by referring to the identifier value of the parameter as defined in the component definition. This validator JavaScript function doesn't return anything. This is not needed because the only return value of a form logic component can be a RoutingResult. (The form engine service ignores any other return objects.) However, if the error container of the fragment contains a message, the form engine service will redirect it to the current form step and a possible RoutingResult of another logic component will be discarded.


Form Handler

The Form Logic components discussed so far have been limited to JavaScript implementations of the action logic. In this part the focus is on a Java implementation, more specifically a Java implementation of a Form Handler. Defining the form handler starts with a registration via the Activator class of your plugin. This class should register  a formlogicProviderComponentDefinition object of the Handler Type (FORM_LOGIC_PROVIDER_TYPE_HANDLER). For example:


formlogicProviderComponentDefinitionImpl def = new formlogicProviderComponentDefinitionImpl(false,formlogicProviderComponentDefinition.FORM_LOGIC_PROVIDER_TYPE_HANDLER,"YOURIDENTIFIERHERE");

	def.setTypeId(formlogicProviderComponentType.class.getName());
	def.setImplementationClassName(YOURHANDLERCLASS.class.getName());
	def.addLanguageLabelMapping(YOURHANDLERCLASS.ERROR_SENDEMAIL_FAILED,"error_sendemail_failed");
	def.addLanguageLabelMapping(YOURHANDLERCLASS.MESSAGE_SENDEMAIL_OK,"message_sendemail_ok");
	def.setTitleLanguageLabel("helloworld.handler.YOURHANDLERCLASS.title");


Language labels needed in the code can be registered via calls to addLanguageLabelMapping() in the bundle Activator. Parameters can be added to the handler by using either the call addRequiredParameter() or addOptionalParameter() in the bundle Activator:


def.addRequiredParameter(
	ParameterDefinition.PARAMETER_TYPE_STRING,      //Type parameter
    YOURCONSTANTSCLASS.PARAM_URL,                   //Name
	"form logic.handler.addtosessionhandler.url");   // Titel of interface

def.setTypeId(formlogicProviderComponentType.class.getName());
def.setImplementationClassName(YOURHANDLERCLASS.class.getName());
def.addLanguageLabelMapping(YOURHANDLERCLASS.ERROR_SENDEMAIL_FAILED,"error_sendemail_failed");
def.addLanguageLabelMapping(YOURHANDLERCLASS.MESSAGE_SENDEMAIL_OK,"message_sendemail_ok");
def.setTitleLanguageLabel("helloworld.handler.YOURHANDLERCLASS.title");


These parameters can be used in the run() method of the handler class.


public RoutingResult run(FormScope scope, Map<String, Object> parameters, Map<String, Object> languageLabels) {
	// All parameters enter the run() method through a Map object
	String urlParameter = (String) parameters.get(YOURCONSTANTSCLASS.PARAM_URL);
	…
}


Pre-handler

A prehandler is a form logic component that has to be executed prior to rendering of the form. It will most likely be used to set values in the forms session that support the rendering of the form such as prefilling and possibly authorization. As descibed in Presentation of Form Entities, the form session is stored in the HTTP session. However, standard frontend XperienCentral JSPs do not have access to the HTTP session, therefore, the HTTP session is retrieved via a Server Side Include directive:


<wm:link var="ssiLink" fromFormSsi="true" ssiObjectId="${element.id}" ssiObjectClassName="nl.gx.forms.wmpformelement.api.FormElement" usehttpsession="true" passOn="wmformid,wmstepid,orgurl" />


Form Router

A key component of the form logic that implements a router is the routing result. The routing result object that is returned from the run() method of the form router has to have a specific routing result type. If the type is not known to the framework, the (warning) message Unknown RoutingResult type. Sending the user back to the referring page will be shown in the log (originating from the FormHandlerHelper logger). The following Routing result types that are supported by the framework are shown in the table below:


File
Value
Description
Required Setter

ROUTE_TO_URL

0

Indicates a routing to a specific URL.

setUrl()

ROUTE_TO_PAGE

1

Indicates a routing to a specific page.

setPage()

ROUTE_TO_STEP

2

Indicates a routing to a specific form step of the current form/form version.

setFormStep()

ROUTE_BACK

3

Indicates a routing to the previous form step.


ROUTE_CURRENT

4

Indicates a routing to the current form step.


ROUTE_FORWARD

5

Indicates a routing to the next form step.


ROUTE_TO_SPECIALPAGE

6

Indicates a routing to a special page.

setSpecialPageLabel()

ROUTE_HTTP_ERROR

7

Indicates an error occured.



Some routing result types require a setter to be executed. The properties that are set are used for normalizing the RoutingResult. In this case, normalizing means that the RoutingResult types are reduced to a subset after the router has been executed. However, not all RoutingResult types are visible in the end result of the form routing action. For example, the RoutingResult that is returned in case of clientside routing will be a normalized routing result.


Group
Internal Name
Normalized Result
1

ROUTE_TO_URL

ROUTE_TO_URL

1

ROUTE_TO_SPECIALPAGE

ROUTE_TO_URL

1

ROUTE_TO_PAGE

ROUTE_TO_URL

2

ROUTE_TO_STEP

ROUTE_TO_STEP

2

ROUTE_BACK

ROUTE_TO_STEP

2

ROUTE_FORWARD

ROUTE_TO_STEP

2

ROUTE_CURRENT

ROUTE_TO_STEP

3

ROUTE_HTTP_ERROR

ROUTE_HTTP_ERROR


Another aspect of the RoutingResult object is setting the end of flow. The form element will automatically start and end a form session. However, if a router is navigates away from the page that renders the form, the form element code will not be executed and the form session will not be closed. If this is the case, the router itself has to notify the framework by setting the endOfFlow flag.


public RoutingResult run(FormScope scope, Map<String, Object> parameters, Map<String, Object> languageLabels) {

	RoutingResult result = new RoutingResult();
	result.setType(RoutingResult.ROUTE_TO_PAGE);
	result.setPage((Page) parameters.get(PARAM_PAGE));
	result.setIsEndOfFlow(true);
	return result;
	}


Bundle activator:


private formlogicProviderComponentDefinition getGoToPageRouterDefinition() {
	Class myClass = GoToPageRouter.class;  formlogicProviderComponentDefinitionImpl def = new formlogicProviderComponentDefinitionImpl(false, formlogicProviderComponentDefinition.FORM_LOGIC_PROVIDER_TYPE_ROUTER, "GoToPage");

	def.setId(myClass.getName());
	def.setName(myClass.getName());
	def.setDescription(myClass.getName());
	def.setTypeId(formlogicProviderComponentType.class.getName());
	def.setImplementationClassName(myClass.getName());
	def.setProperties(new Hashtable<String, String>());
	def.addRequiredParameter(ParameterDefinition.PARAMETER_TYPE_PAGE, GoToPageRouter.PARAM_PAGE, "page");
	def.setTitleLanguageLabel("wmpform logic.router.goto_page_router.title");
    return def;
    }


Back to top



Advanced Development


Advanced Form Fragment Development

As for all plugin's, the bundle Activator defines the set of components via component definitions specified in the current plugin. The FormFragmentComponentDefinition extends the general ComponentDefinition class used for adding components through a plugin (for example a custom element component). The definition specification in the Activator is similar to other XperienCentral components:


FormFragmentComponentDefinitionImpl def = new FormFragmentComponentDefinitionImpl(false); def.setId(CustomFragmentImpl.class.getName());
def.setName(pluginConstants.plugin_ID + " " + CustomFragmentImpl.class.getName());
def.setDescription("Implements the " + pluginConstants.plugin_ID + " " + CustomFragmentImpl.class.getName());
def.setTypeId(FormFragmentComponentType.class.getName());
def.setProperties(new Hashtable<String, String>());
def.setInterfaceClassNames(new String[]{Component.class.getName(), FormFragmentComponent.class.getName()});
def.setInstanceClassName(CustomFragmentImpl.class.getName());
def.setImplementationClassName(CustomFragmentImpl.class.getName());
def.setControllerClassName(CustomFragmentController.class.getName());
def.setEntityClassNames(new String[]{CustomFragmentImpl.class.getName()});
def.setPersistenceManagerClassName(DefaultJcrPersistenceManager.class.getName());
def.setEntityFactoryClassName(DefaultJcrEntityFactoryImpl.class.getName());
def.setInterfaceClassName(CustomFragment.class.getName());
def.setIcon("img/customFragment.png");
def.setLanguageLabel("panel.wmpformfragments.customFragment");
def.setPresentationScope(presentationScope);
def.setFragmentType(nl.gx.forms.wmpformapi.pluginConstants.FRAGMENT_TYPE_INPUT);
def.setRanking(ranking);
def.setWrapperClassNames(new String[]{CustomFragmentImpl.class.getName()}); ComponentDependency formDAODependency = createDependency(FormManager.class.getName()); ComponentDependency entityManagerDependency = createDependency(EntityManager.class.getName());
def.setDependencies(new ComponentDependency[]{
	formDAODependency, entityManagerDependency});
return def;


Some setters, however, are specific to the Interactive Forms component, for example FragmentType and Ranking. The ranking is an arbitrary integer that is used to order the fragments in the form maintenance panel.

FormFragment Interface Types

The fragment type is one of the static strings defined in the pluginConstants file of the wmpformapi. Typically, of type static, input or group.


Constants
Value
Description

FRAGMENT_TYPE_INPUT

input

A fragment type that supports input.

FRAGMENT_TYPE_GROUP

group

A type of fragment that is used for combining multiple fragments (for example subform fragments).

FRAGMENT_TYPE_STATIC

static

A fragment type that doesn't support input, for example, a paragraph form fragment.


Form fragments are grouped according to their types in the panel.

Form Fragment Ordering in the Interface

The ordering of the fragment components is defined to a ranking within a fragment type group. The standard set of fragments is summarized in the following table.


Name
Ranking
Type

textInputComponent

10

FRAGMENT_TYPE_INPUT

numberInputComponent

20

FRAGMENT_TYPE_INPUT

dateInputComponent

30

FRAGMENT_TYPE_INPUT

passwordInputComponent

40

FRAGMENT_TYPE_INPUT

textareaInputComponent

50

FRAGMENT_TYPE_INPUT

radioComponent

60

FRAGMENT_TYPE_INPUT

checkboxListComponent

70

FRAGMENT_TYPE_INPUT

dropdownListComponent

80

FRAGMENT_TYPE_INPUT

fileUploadComponent

90

FRAGMENT_TYPE_INPUT

emailInputComponent

100

FRAGMENT_TYPE_INPUT

columnsComponent

10

FRAGMENT_TYPE_GROUP

sectionComponent

20

FRAGMENT_TYPE_GROUP

formSectionComponent

30

FRAGMENT_TYPE_GROUP

repeatComponent

40

FRAGMENT_TYPE_GROUP

buttonInputComponent

10

FRAGMENT_TYPE_STATIC

backButtonInputComponent

20

FRAGMENT_TYPE_STATIC

paragraphComponent

30

FRAGMENT_TYPE_STATIC

overviewInputComponent

40

FRAGMENT_TYPE_STATIC


Influence of the Fragment Presentation on the Backend

If you introduce new presentation(s) for existing or custom form fragments or form steps, keep the following in mind: In the XperienCentral platform, the Interactive Forms component uses the same presentation logic for both the frontend and the backend interfaces. Therefore, the JSP presentation files have to take into account events that might occur in backend. For example:


<forms:fragmentDiv fragment="${formFragment}" fragmentType="formsection">
	<c:choose>
		<c:when test="${empty formFragment.formSection && mode == 'preview'}">
			<fmt:message key="panel.wmpformfragments.overview.canvastext.noformsectionselected" />
		</c:when>
		<c:when test="${empty formFragment.formSection.activeVersion && mode == 'preview'}">
			<fmt:message key="panel.wmpformfragments.overview.canvastext.formsectioninactive" />
		</c:when>
		<c:otherwise>
			<c:if test="${not empty formFragment.formSection.activeVersion}">
				<wm:render object="${formFragment.formSection.activeVersion}" fromFragment="true" />
			</c:if>
		</c:otherwise>
	</c:choose>
</forms:fragmentDiv>


Form import/Export

Importing and exporting forms, form fragments and/or form logic entities is implemented as a standard component in the Interactive Forms component. If custom components are introduced, the  FormManager has to be able to find the entities that should be picked up during the export/import process. If specific custom properties are stored using the @Child annotation, the import/export process will pick up these properties automatically.

The @Child annotation can be used to indicate that the object should be stored as a reference property not as a child property of this entity. As a result, the physical location of the object will be such that there will be a parent-child relation between the entity and the object to which is referred to by the @Child annotation. In the case of the JCR, this means that the node that corresponds to the entity is a parent node of the node that corresponds to the object of the property marked with the @Child annotation.

Clientside Routing: AJAX Development Using the JSON Interface

In some projects, you need to obtain a form-post action without refreshing the complete page. Clientside routing combined with JavaScript AJAX functionality helps you (as a developer) to meet this requirement. When clientside routing is used, a typical JSON response looks like this:


{ "routingResult": { 
	"type":
	"2", 
	"step":
	"garage", 
	"hasErrors":
	"false" } 
}


The first part always consists of the RoutingResult and type. Also, the hasErrors remark will be present in the response. Depending on the RoutingResult type, the center of the response (that is, step) will have a different format. The following table contains a breakdown of the response according to RoutingResult type:


RoutingResult type identifier

Type identifier

Value

ROUTE_TO_SPECIALPAGE     

url

RoutingResult.getUrl()

ROUTE_TO_PAGE  

url

RoutingResult.getUrl()

ROUTE_TO_URL     

url

RoutingResult.getUrl()

ROUTE_CURRENT   

Step

RoutingResult.getFormStep().getIdentifier()

ROUTE_BACK         

Step

RoutingResult.getFormStep().getIdentifier()

ROUTE_FORWARD  

Step

RoutingResult.getFormStep().getIdentifier()

ROUTE_TO_STEP    

Step

RoutingResult.getFormStep().getIdentifier()

ROUTE_HTTP_ERROR

httpError

RoutingResult.getHttpErrorCode()

ROUTE_HTTP_ERROR

httpErrorMessagege

RoutingResult.HttpErrorMessage()


The clientside JavaScript framework is not up to date when the form rendering is bypassed via the AJAX call. The getClientSideFrameWork() method of the Formelement.java class helps you retrieve the right JavaScript functions, for example after routing to a new step.


For a complete AJAX solution, the retrieval of form(steps) has to be implemented as well. Form HTML code can be retrieved based on a URL that includes elementId and presentationid (for example, http://mywebsite.com/MyPage.htm?elementId=418808&presentationid=415177&view=ajax ).


Back to top



Presentation of Form Entities

If a custom form fragment is added, the presentation scope can be set in the component definition in the activator class of your plugin definition.setPresentationScope(presentationScope. This presentation scope is the reference value that is used in the descriptor xml file that comes with the presentation jspf. If you only want to add presentations for standard form entities, for example, form, form step, email input fragment, and so forth, the presentation scope is fixed. The following table gives an overview of presentation scopes for form entities:


Entity

Name

Scope

FormElement


FormsElement

FormStep


FormStep

FormFragment

TextInput

FormFragmentTextInput

FormFragment

NumberInput

FormFragmentNumberInput

FormFragment

DateInput

FormFragmentDateInput

FormFragment

PasswordInput

FormFragmentPasswordInput

FormFragment

EmailInput

FormFragmentEmailInput

FormFragment

TextArea

FormFragmentTextArea

FormFragment

FileUpload

FormFragmentFileUpload

FormFragment

Columns

FormFragmentColumns

FormFragment

Section

FormFragmentSection

FormFragment

Button

FormFragmentButton

FormFragment

Overview

FormFragmentOverview

FormFragment

BackButton

FormFragmentBackButton

FormFragment

RadioList

FormFragmentRadioList

FormFragment

CheckboxList

FormFragmentCheckboxList

FormFragment

DropDownList

FormFragmentDropDownList

FormFragment

FormSection

FormFragmentFormSection

FormFragment

RepeatFormSection

FormFragmentRepeatFormSection

FormFragment

Paragraph

FormFragmentParagraph

FormFragment

RepeatFormSectionCountFragment

FormFragmentRepeatFormSectionCountFragment


The presentation of form entities is supported by a couple of tags. These tags are used throughout the standard presentation that is shipped with the Interactive Forms component. The following table describes these tags.


Tag

Description

form

Renders the HTML form tag plus contains some required hidden fields. The wmpform class is required for the clientside framework JavaScript library (validation.js).

fragment

Renders the HTML of a single fragment calling other tags (messages, fragmentDiv, fragmentDisplay, fragmentLabel).

fragmentDisplay

If a precondition for a fragment is defined, the string style="display:none is added to the fragment <div>.

fragmentDiv


fragmemtLabel


fragmentValue


messages

A helper tag for displaying messages.

xmlescape

A helper tag for XML escaping.

endsession

The endSession request attribute is set and handled by the render method of the FormElement java class.


Form step presentation:


<forms:form element="${presentationcontext.element}" formStep="${formStep}">


Form fragment presentation:


<forms:fragment fragment="${formFragment}">
	<input type="text" ${extra} name="${formFragment.nestedPath}" id="wmformfragment_${identifier}" value="<forms:fragmentValue nestedPath="${formFragment.nestedPath}" />" ${widthAttribute} ${maxLengthAttribute} /><p />
</forms:fragment>



  • The HTML of the form (element) is not the only factor that affects the rendering for the frontend and the backend. The CSS also influences the appearance of the form. Without specific changes to your plugins, the form will use the CSS of the frontend on the frontend and the CSS of XperienCentral on the backend (in Interactive Forms). It is possible to align the used CSS on the backend with the used CSS on the frontend.
  • With the default presentation, it is possible to perform certain actions with form elements. For example, to drag and drop each form element, or to drag other elements into the Columns element. When creating your own HTML for a form element, it is important to examine the default presentations of that element and use the same HTML structure as the default presentation.



Back to top



The Form Engine

The form engine (service) handles the execution of a form. Actions like form session building/preparation are performed by this engine. Further, the form logic execution process is also performed by the form engine service by calling the run() method of the attached handlers.

The Form Scope

The form values and other state variables are stored server-side in what is referred to as form scope. The form scope is a hierarchy of scopes; for each BasicFormFragmentContainer, a new nested and named scope exists. This class represents a single node in such a scope, and is the interface that is used by most form logic components for accessing the scope. Information not found in the current scope is automatically looked up in the parent scope.

The Form Engine Session

The FormEngineSession object is the root of the form engine session. Within this object is a tree of FormScope, FragmentScope and UploadFragmentScopes that hold the submitted values and the state of the various parts of the form. Apart from these, this class holds the identifier of the last submitted step, a reference to the BasicFormVersion, the last access time (for timeout), the FormSessionContext and an identifier to the form engine session itself.

The FormElement (Java implementation class) creates and ends the form session. When an interactive form is rendered on the frontend, the FormElement.Java code is the entry point. The first thing the FormElement does (in its render() method), is set up the session. Through the FormEngineService, a FormEngineRequestHelper object will be asked to retrieve the FormEngineSession. The getOrCreateFormEngineSession() method of the RequestHelper is used for this purpose. As a result, an existing session or new session will be returned and stored in the HTTPSession. The session object contains information such as formIdentifier, lastSteopIdentier, lastAccessTime, etc.

After setting up the FormSession, the Formelement.java object tries to execute the preparation of the current form step by means of the following code:


prepareFormFragmentContainer(formStep, getSession().getFormScope())


In this method call on the FormEngineService, the current step identifier and formScope are returned as parameters. The returned result of this method is a RoutingResult object. However, the result is ignored because the prehandling action doesn't require a routing effect. The actual result of this method call is the prefilling of form fragments and the execution of logic components that are assigned as prehandlers.

The cleaning up of the form session is initialized as follows: when the presentation JSP of the formElement (that is, showFormElement.jspf) is processed, the last step of a form sequence will be deducted. Next, the endsession tag is executed.


<c:if test="${isLastStep && empty element.formStep.form logic}">
	<forms:endsession />
</c:if>


The endsession tag wil set an endSession variable with request scope.



<c:set var="endSession" value="true" scope="request" />


As the FormElement.java code execution continues, this value is retrieved from the request and the session is closed (by calling the endFormSession() method of the FormEngineRequestHelper. This only happens when the session is not ended by the form handling process (that is, FormHandlerServlet). 


Language Setting During Form Rendering

When the render() method of the form element object is called, the language is retrieved from the page version of the current page. The metaTagValue of the language is then added to the request. While setting up the session, the metaTagValue of the language is also added to the FormScope via the setLocale() method. The language value of the form scope is retrieved throughout the Interactive Forms component, for example when executing a form logic component and when building the clientside JavaScript framework.

FormEngineRequestHelper

The form engine service is decoupled from the servlet engine through the introduction of the FormEngineRequestHelper. The form engine request helper communicates with the servlet API and the form engine service and manages the mapping of form engine sessions to HTTP sessions. In other words, the FormEngineService is agnostic with respect to the interface from which it is approached; it has a clean Java API that is not explicitly coupled to external APIs. This makes it properly testable and much more flexible. However, the most common use case for the form engine service is for it to be used on a website via HTTP. In order to facilitate that use-case, this class provides the necessary coupling between the servlet API and the form engine.

FormEngine Servlet

The FormHandlerServlet handles form POSTs. Also, when receiving a GET request, a dump of the active form engine sessions is shown, but only on the generator when the user is logged in in the backend.

Several use cases exist for POSTing a form, but they all have the following in common:

  • The identifier of the form step is in the request parameter wmstepid.
  • The ID of the form version is in the request parameter wmformid.
  • For each fragment on the step that is submitted, one or more values may be posted. These have names that are dot-expressions relative to the form step. The dot-expression for a fragment with identifier "fragment" on a form section that is rendered in a form section fragment with identifier "section" on a form step with identifier "step1", has dot-expression section.fragment, which corresponds to the server-side nested path step1.section.fragment.
  • For each fragment that is submitted, the condition value indicating whether the fragment is rendered and handled can also be updated. This is done by submitting the boolean value of the condition (true or false) with a parameter with the name equal to the nested path described above, for example step1.section.fragment.condition=true.

For each request, the form engine session is updated to match the submitted values and conditions. Also, the last submitted step and the last access time are updated. What happens thereafter is controlled by several request parameters. The following use cases are defined:

Use Case 1: Default Form Posting

By default, the step is submitted and validated. If validation errors occur, the user is redirected back to the form in order to correct the error(s). If validation passes, the form engine checks whether the submitted values have caused preconditions for displaying fragments on this step to have changed from false to true. If that is the case, the user is also redirected back to the form in order to fill in the newly visible fragments. If no conditions have changed from false to true, the form is handled — All form logic components for the form step are recursively executed. If an error occurs, the user is redirected back to the page. If no error occurs, but a RoutingResult is returned by the form logicComponents, the RoutingResult is honored and the proper redirect is sent. By default, the user is routed to the next form step. If no next step is available, the user is redirected to the step that was just submitted.

Use Case 2: Client-side Routing

This use case is similar to what happens in use case 1, but instead of redirecting the user, a JSON response describing the RoutingResult is sent. For example:


script type="text/JavaScript" >
$(document).ready(function() {
	$('.wmpform').submit(function() {
		$('#clientsideRouting').attr('value', 'true');
		$.post('/web/pluginservlet/nl.gx.forms.wmpformengine.servlet',
$('.wmpform').serialize());

	});
});
</script>


The type that is returned is the RoutingResult type as described in Clientside routing: AJAX development using the JSON interface. The JavaScript code that handles the JSON response should have a decision tree that maps these types. In order to activate client-side routing a parameter clientsideRouting=true has to be present in the request. The following JQuery script prints the JSON response on the browser:

Use Case 3: Validate a Single Fragment

When the request parameter validateFragment is present, only a single fragment is validated. That fragment is identified by the value of the validateFragment which is a nested path as described above. The result of the validation is returned in the form of a JSON response. For example:


<pre>
{ "validationResult": {
"fragment": "wmfragment_2",
"messages": [
],
"errors":
[
	{"code": "MISSING", "message": "Dit veld is verplicht" }
]
} }
</pre>


Use Case 4: Go Back One Step

When a request parameter with name wmback is present after updating the values, the user is redirected to the previous form step. The value of the parameter is irrelevant because it is typically a button (the value is the value of the button). This can be used in conjunction with clientsideRouting.

Form Session Ending

If the FormHandlerServlet is run in the default mode, the form logicComponents belonging to the current step are executed. Some form logicComponents (for example the GotoPageRouter) will navigate out of the form context to another page. In these cases, the form session should be ended by the FormHandlerServlet since the form is longer being rendered  and therefore the formElement.java code will not handle the session. These particular form logicComponents should indicate that form session cleanup is needed by using the setIsEndOfFlow() method of the RoutingResult.


RoutingResult result = new RoutingResult();
...
result.setIsEndOfFlow(true);
return result;


Next, while FormHandlerServlet is analyzing the RoutingResult, it will discover that isEndOfFlow() equals "true". The FormSession will then be ended by retrieving the getRequestHelper from the FormEngineService and executing the endFormSession() method.


Back to top



Other Topics


Approaching Other Forms from the Frontend

It’s possible to retrieve information from other forms that are currently active (and thus available via the request object). In the following code example a form logic component is accessing other forms:


public RoutingResult run(FormScope scope, Map<String, Object> parameters,Map<String, Object> languageLabels) {

	FormEngineRequestHelper helper = getFormEngine().getRequestHelper(request);
	// Retrieve the form identifiers via the helper object
	Set<String> activeFormVersionIdentifiers = helper.getAllSessions();
	for (String activeFormVersionIdentifier : activeFormVersionIdentifiers) {

		//each active form has its own session
		FormEngineSession formEngineSession = helper.getFormEngineSession(activeFormVersionIdentifier);
		//this session can be used to retrieve form information            
		formEngineSession.getFormVersion() .getSteps();
	}



As soon as the right step is retrieved, it could even be handled from the form logic component. Here’s how to alter a step fragment value and submit a step:



//fix the step that will be addressed
formEngineSession.setLastStepIdentifier(step.getIdentifier());
// alter a value
formEngineSession.getFormScope().setFragmentValue("step.identifier", "value");
// submit the form.
RoutingResult result = formEngine.handleStepSubmit(formSession);   
}