Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / XHTML

AJAX enabled collapsible form section

3.57/5 (4 votes)
29 Dec 2008CPOL6 min read 41.1K   199  
An AJAX control that can hide or show a form section including handling validators nested in the section.

Introduction

There are many cases where we may need to implement complex forms that contain multiple sections that can be hidden or shown depending on the user’s entries.

The issue we encounter in such scenarios is that the section we want to hide or show may contain validators that must be disabled in order to let the form to post back. This can be easily achieved using a server side code, but it involves a round trip to the server which we would like to avoid for performance reasons.

To disable validators with JavaScript, we must keep a reference to all of them and, when hiding a part of the form, we must disable them all, and when we show it, we must re-enable them back.

This control is all about making all this work transparently to the developer. The only thing developers will need to do is to add this to their form and embed all the controls and their validators in it. It then handles all the necessary enabling/disabling of nested validators and also, shows/hides all controls in it.

For example, consider the following form:

collapsed.PNG

When the user clicks on the check button, we would like the second part of the form to be displayed as well:

the_entire_form.PNG

Knowing that the three new displayed fields are mandatory, if we do not disable their validators when we hide the bottom part of the form, it won’t post back, and actually, it won't even display the validation error messages since those will be contained in a hidden DIV.

Using the code

From the client side perspective

This control is an AJAX control which means that each instance of the control on the page will generate a corresponding AJAX (JavaScript) object instance based on the component defined in the ‘AjaxCollapsibleFormSection.js’ file.

Basically, it is a component that has a few properties and the necessary algorithms to show and hide a form section. The reason it has been implemented using the AJAX component model is that we want to be able to have more than one collapsible section in the file and thus, each instance, on the JavaScript side, should have its own list of validators and other properties. The best way to do it, therefore, is to have an object in the JavaScript that represents a section with all its properties embedded (this is merely the encapsulation principle).

To make a control AJAX enabled, we must implement the IScriptControl interface which contains two members. One to get the references to JavaScript files, and the other a list of ScriptControlDescriptor objects.

The ScriptControlDescriptor for this control is pretty simple as it contains the following properties:

  • Validators: an array of all validator IDs.
  • GroupName: an arbitrary string used to group sections together in one page and make their appearance exclusive (when one section is shown, all others with the same group name are hidden).
  • Exclusive/Inclusive
  • IsVisibleSection
  • ShowHideControl: This is an ElementProperty which means that it will translate to a JavaScript object reference pointing to a check box whose state will show or hide the section.
  • ShowControl: A reference to a radio button that will show the section if checked.
  • HideCoontrol: A reference to a radio button that will hide the section if checked.

Following is the code that implements the IScriptControl interface:

C#
IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
{
    ValidateShowHideControls();
    ScriptControlDescriptor desc = 
      new ScriptControlDescriptor("SABBEL.Web.Controls" + 
      ".Ajax.CollapsibleFormSection", this.ClientID);
    string[] validatorsArray = new string[_validators.Count];
    _validators.CopyTo(validatorsArray, 0);
    desc.AddProperty("Validators", validatorsArray);
    desc.AddProperty("GroupName", GroupName);
    desc.AddProperty("Exclusive", Exclusive); 
    desc.AddProperty("Inclusive", Inclusive);
    desc.AddProperty("IsVisibleSection", IsVisibleSection);
    if (ShowHideControl!=null)
        desc.AddElementProperty("ShowHideControl", ShowHideControl.ClientID);
    if (HideControl != null)
        desc.AddElementProperty("HideControl", HideControl.ClientID);
    if (ShowControl != null)
        desc.AddElementProperty("ShowControl", ShowControl.ClientID);
    yield return desc;
}

IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
{
    ScriptReference reference = new ScriptReference(
      "SABBEL.Web.Controls.Ajax.AjaxCollapsibleFormSection.js", 
      "SABBEL.Web.Controls.Ajax"); 
    yield return reference;
}

On the JavaScript side, the “SABBEL.Web.Controls.Ajax.CollapsibleFormSection” class has a few methods. The most important one is the ShowHide that will show or hide the section depending on the first parameter value. This method will, in turn, call “ResetValidators” to either enable them or disable them depending on whether we are showing or hiding the section.

Another method “InitializeCollapsibleForms” is registered to start as soon as the AJAX Toolkit is loaded. This is done by calling the add_load method.

JavaScript
Sys.Application.add_load(InitializeCollapsibleForms);

That is, once the whole page is loaded, it will check all the sections in the page, and hide or show them initially depending on the state of the controls (check boxes or radio buttons) that control them.

In the JavaScript file, you can also find the functions called when the check box or radio buttons are checked or unchecked. The names of these functions can be changed by passing the name to the section control. There are three functions that can be renamed (so that you can rewrite them and add any extra pre/post show/hide events or processing):

Property name

Default value

Signature of the JS function

ShowEventHandler

ShowForm

function showForm(collapsableSectionId)

HideEventHandler

HideForm

function hideForm(collapsableSectionId)

ShowHideEventHandler

ShowHideForm

function showHideForm(showHideControl, collapsableSectionId)

The signature must be the same as in the table above if you decide to rename the functions and rewrite them. You can start from the default implementations provided in the JavaScript file that comes with the server control.

From the server side perspective

On the server side, this control parses all child controls looking for any validator. All validator client IDs will be kept in an array and passed to the AJAX object reference through its ‘Validators’ property. If the property “Recursive” is set to true, the control will parse child controls recursively. This may have a performance impact, and for this reason, the Recursive property is set to false, by default.

Also, it will add a JavaScript event handler to the check box or the radio buttons that has been associated with the section.

And, that’s it. As you can see, most of the work is done on the client side with the AJAX component, as described above.

How to use this control

In the page, just surround your form section that you want to hide and show on demand by the AjaxCollapsibleFormSection control, and specify which control will trigger the show and/or hide event; in the example below, the control is the CheckBox (checkbox1).

ASP.NET
<asp:CheckBox runat="server" 
      Text="show this section" ID="checkbox1" /> 

<cc1:AjaxCollapsibleFormSection ID="AjaxCollapsibleFormSection1"
  ShowHideControlName="checkBox1"
  GroupName="Group1"
  Exclusive="true"
  ShowHideEventHandler="showHideFormExclusive"
  runat="server"> 


<asp:RequiredFieldValidator ID="rfvFirstName" 
  runat="server" 
  ErrorMessage="First Name is mandatory"
  ControlToValidate="firstName"/>

First Name: <asp:TextBox runat="server" ID="firstName">
</asp:TextBox><br />

<asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
  ErrorMessage="Last Name is mandatory" 
  runat="server" ControlToValidate="lastName"/>
  Last Name: <asp:TextBox runat="server" ID="lastName">
</asp:TextBox><br />
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" 
  runat="server" ErrorMessage="Street address is mandatory" 
  ControlToValidate="address" /> 
Street Address: <asp:TextBox runat="server" ID="address">
</asp:TextBox><br /> 

</cc1:AjaxCollapsibleFormSection> 

The sample application

In the sample application, there are examples of the usage of exclusive sections and recursive controls for the case when you have a nested user control in the section.

Conclusion

This control has been very useful for me and our team as we could very easily make our forms more dynamic without the cost of posting back the page every time we needed to collapse or show a particular part of it because of the validators. At first, we started to write JavaScript in the old way with lists of arrays and inline JavaScript to keep track of validator IDs etc. However, this solution is much more elegant and reusable, and of course, easier to maintain as it is object oriented.

This is a good example of what we can achieve using AJAX enabled controls. There is much to say about AJAX controls, but hopefully, this one can be a good starting point.

One enhancement would be to decouple this control from check boxes and radio buttons, and allow any other web control to trigger it; for instance, we could have a link that will show the section, and another link on the page that will hide it. Or it could be a button, an image…

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)