Client side multipage forms

Client side multipage forms

Posted on November 21, 2011 0 Comments

The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.

Telerik Sitefinity CMS comes with a Forms module featuring different widgets like checkboxes, multiple choices and so on. The widgets support validation, customization of titles and messages. This allows for the easy creation of surveys and questionnaires.
There are cases when large surveys or questionnaires are not usable to set on one page. This can be solved with multipage forms using the solution provided below.
Note: the following sample works with Sitefinity 4.3 and above.

 

I’m going to show how to create multipage forms with the help of the current Sitefinity API and the layouts that are available in the Forms module. The idea is to create a “dynamic” layout which can show or hide its placeholders (“pages”).  Each layout widget is actually a simple .ascx file with a specific html structure for recognition of the placeholders. It looks like:


<div runat="server" class="sf_cols">
    <div runat="server" class="sf_colsOut sf_1col_1_100">
        <div runat="server" class="sf_colsIn sf_1col_1in_100 myCustomCSSClass">
  
        </div>
    </div>
</div>

What we’re going to do is to create a custom user control (“MultipageFormsLayout.cs”) that generates the placeholders (the two DIV elements with classes “sf_colsOut” and “sf_colsIn”). The idea behind this is that we need to know which form widget is placed in which placeholder (“page”). The rest is easy - when the user clicks submit we’re going to validate only the widgets in this “page”. If there are no validation errors we hide the current placeholder and show the next one. There are no postbacks, everything is done on the client with JavaScript.
The layout file will look like this:

<%@ Control Language="C#" %>
<%@ Register Assembly="SitefinityWebApp" Namespace="Telerik.Sitefinity" TagPrefix="cl" %>
<cl:MultipageFormsLayout runat="server" NumberOfPages="3" />

You can see the property “NumberOfPages” – this sets the number of placeholders (“pages”) to be rendered.
The MultipageFormsLayout control
This is more or less a composite control, just which it inherits from the “HtmlGenericControl” and implements the IScriptControl interface. In order to have the placeholders always generated the Controls property is overridden:

public override ControlCollection Controls
{
    get
    {
        this.EnsureChildControls();
        return base.Controls;
    }
}

Then in the CreateChildControls we add the placeholders:
protected override void CreateChildControls()
{
    this.Controls.Clear();
    this.placeholders.Clear();
    this.CreateControlHierarchy();
}
Where the CreateControlHierarchy method generates placeholders:

for (var i = 0; i < this.NumberOfPages; i++)
{
    var placeHolder = this.CreatePlaceholder(this.InnerCssClass, this.OuterCssClass);
    if (i > 0 && !this.IsBackend())
    {
        //if rendered in the frontend we show only the first placeholder
        placeHolder.Style[HtmlTextWriterStyle.Display] = "none";
    }
    this.Controls.Add(placeHolder);
    this.placeholders.Add(placeHolder);
}

And the next important part is that we add the client IDs to the ScriptDescriptor of the control, so they are available in the JavaScript component responsible for the client side operation – show/hide/validation/etc.

public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
    var descriptor = new ScriptControlDescriptor(this.GetType().FullName, this.ClientID);
    descriptor.AddProperty("numberOfPages", this.NumberOfPages);
    descriptor.AddProperty("placeholders", this.placeholders.Select(c => c.ClientID));
 
    descriptor.AddElementProperty("formsControlElement", this.GetParentFormsControl().ClientID);
 
    return new[] { descriptor };
}
Now after we have the IDs of the placeholders on the client we’ll associate each form widget with a page:
In MultipageFormsLayout.js
_buildPageControlsMap: function () {
        var placeholders = this.get_placeholders();
        var fieldControlsMappings = $FormManager._controlIdMappings;
        for (var i = 0, len = placeholders.length; i < len; i++) {
            var controlsIds = [];
            this._pageControlsMap[i] = controlsIds;
 
            var placeHolderElement = $get(placeholders[i]);
            for (var key in fieldControlsMappings) {
                var fieldControlId = fieldControlsMappings[key];
                //check if the control is a child of the placeholder
                var el = jQuery(placeHolderElement).find("#" + fieldControlId);
                if (el.length == 1) {
                    controlsIds.push(fieldControlId);
                }
            }
        }
    },
The form widgets are actually field controls. FieldControl is a base type for all controls in the backend of Sitefinity that supports validation, client side binding, configuration that can be persisted and much more. FieldControls are registered in a global manager called FormManager and this manager “knows” which the field controls on the current page are. The manager can be accessed with the global variable “$FormManager”.
In the aforementioned code we actually check child elements of each placeholder and then associate them with the field controls (widgets) found there. So we create a mapping between each placeholder (“page”) and its controls.
The next step is to override the default behavior of the submit button of the form:
_overrideSubmitButtons: function () {
        var submitButtons = jQuery(this.get_formsControlElement()).find(":submit");
        if (submitButtons.length > 0) {
            // from the first we take the original click handler
            var submitButton = jQuery(submitButtons[0]);
            var functionBody = submitButton.attr("onclick");
            this._originalSubmitHandler = new Function(functionBody);
 
            // then we override all the buttons
            for (var i = 0, len = submitButtons.length; i < len; i++) {
                var submitButton = jQuery(submitButtons[i]);
                submitButton.attr("onclick", null);
                submitButton.click(this._showNextPageDelegate);
            }
        }
        else {
            throw new Error("Unable to override the submit buttons!");
        }
},
After this operation the submit button will call the function that will validate the controls and show the next page:
showNextPage: function () {
        var validationResult = this.validatePage(this._currentPageNumber);
        if (validationResult) {
            if (this._currentPageNumber >= this._numberOfPages - 1) {
                return this._originalSubmitHandler();
            }
            else {
                this.hidePage(this._currentPageNumber);
                this._currentPageNumber++;
                this.showPage(this._currentPageNumber);
            }
        }
        return false;
},

Of course there is a downside of this approach – layouts for different number of pages should be registered.

Having all this implemented we have to register the layout widget in Sitefinity and make it available to the Forms module.
1.    Extract the zip file into the root of your website, it will create a folder named “MultipageLayout” with the required files.
2.    Build your project
3.    Go to Administration -> Settings
4.    Open the Advanced Settings
5.    Navigate to the Toolboxes section and expand it – Tooboxes -> Page Layouts ->Sections-> Two columns->Tools
6.    Create new with the following settings:
Control CLR Type or Virtual Path: Telerik.Sitefinity.Web.UI.LayoutControl, Telerik.Sitefinity
Name: 3pages
Title: 3 pages
Layout Template:  ~/MultipageLayout/MultipageLayout.ascx
7.    Save changes

Now you can create your form. Drop the 3 pages layout and then drop the form widgets in the different placeholders (“pages”). Make sure the Submit button is not dropped in the multipage layout because it will be hidden as part of a page.

Put a form widget on some page and publish it. When you open the page you’ll see the multipage page form there.

The zip file with the source can be downloaded from here

Screenshots:

Advanced settings

Advanced settings - layout toolbox

Layout registration

Advanced settings - layout registration

Form with 3 pages layout
Layout with 3 pages

3 pages layout selected

Form with 3 pages layout selected.

Form with 3 pages layout and widgets

Form with widgets 

progress-logo

The Progress Team

View all posts from The Progress Team on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.

Comments

Comments are disabled in preview mode.
Topics

Sitefinity Training and Certification Now Available.

Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.

Learn More
Latest Stories
in Your Inbox

Subscribe to get all the news, info and tutorials you need to build better business apps and sites

Loading animation