Cross-Field Validation in Web Forms for Marketers

September 27, 2016

Blog | Development | Cross-Field Validation in Web Forms for Marketers
cross field validation for web forms for marketers-blog-post-header

Recently, I was asked to create useful custom validation on a Sitecore Web Forms for Marketers form (WFFM). What distinguished this task from my previous experience with custom WFFM validation is that this particular validation required access to another field on the form and was dependent on the other field’s inputted value. The form relied on user input to setup an appointment time and consisted of multiple text fields gathering user information. The two fields of interest, however, were a date picker and a droplist field with various options to select a time window.

The first part of validation involved confirming that the date selected occurred either today or sometime in the future. The second portion of this task involved retrieving the date selected and if that value equaled the current date, validation would continue to determine whether the time window selected occurred later than the current time. I found a number of potential ways to approach this cross-field validation and found it to be an informative look into how WFFM validation operates.

I began by implementing standard custom validation. Custom Web Forms for Marketers fields were created – the FutureDatePicker field extended the DatePicker field and TimePickerDropList extended the DropList field. In addition, a DateInFutureValidator and TimeInFutureValidator were both created. Of course, each validator was added as validation on the respective custom field in Sitecore.

Details regarding how to properly configure these custom fields and validators are beyond the scope of this post, as I intend to focus specifically on the issue of cross-field validation. There are plenty of resources online describing in detail the creation of custom WFFM fields and validators, with official documentation at: https://doc.sitecore.net/web_forms_for_marketers/working_with_actions_and_validations.

 

Solution One:

Once I began development on this ticket, I had a difficult time determining how to actually access the other field value in the form. The challenge came when I realized that within my TimeInFutureValidator, there was no built in manner to retrieve information from another field within the form. After all, that information would not be part of the scope of a custom validator for what is a separate field. The validation for the date field was easy enough, however I came up with a creative solution to accessing that date field value in order to proceed with validating the selected time window from the droplist.

My initial method of handling this dilemma was to take advantage of the fact that a custom field had been created for FutureDatePicker by overriding the OnInit method and setting a custom attribute value on the textbox itself. This solution then enabled me to search all page controls from the TimeInFutureValidator and locate the custom control that contained the newly created custom attribute. That control of course would be the date textbox and the selected date value could then be retrieved to continue with time window validation.

 

Example Code:

 

protected override void OnInit(EventArgs e) { 
// Adding a custom Attribute to the textbox 
this.textbox.Attributes["data-validation"] = "DateForValidation"; 

// The following is default from the WFFM Sitecore.Form.Web.UI.Controls.DatePicker.cs file 
this.textbox.CssClass = "scfDatePickerTextBox"; 
this.help.CssClass = "scfDatePickerUsefulInfo"; 
this.generalPanel.CssClass = "scfDatePickerGeneralPanel"; 
this.title.CssClass = "scfDatePickerLabel"; 
this.textbox.TextMode = TextBoxMode.SingleLine; 
this.Controls.AddAt(0, (Control)this.generalPanel); 
this.Controls.AddAt(0, (Control)this.title); 
this.generalPanel.Controls.AddAt(0, (Control)this.help); 
this.generalPanel.Controls.AddAt(0, (Control)this.textbox); 
} 

Overriding OnInit method to apply custom attribute on textbox

 

RetrieveAllControls(this.Page); 
WebControl datePickerControl = null; 
foreach (Control contrl in controlList) { 
if (contrl is WebControl) { 
var wc = contrl as WebControl; 
if (wc.Attributes["data-validation"] != null && wc.Attributes["data-validation"] == "DateForValidation") { 
datePickerControl = wc; 
break; 
} 
} 
} 

Search all page controls for textbox with custom attribute

 

// Retrieve all page controls 
private void RetrieveAllControls(Control control) { 
foreach (Control ctr in control.Controls) { 
if (ctr != null) { 
controlList.Add(ctr); 
if (ctr.HasControls()) { 
RetrieveAllControls(ctr); 
} 
} 
} 
} 

Method to retrieve all controls

 

Solution Two:

 

After successful implementation of this solution, I was still curious about other methods of cross-field validation and finally came across two other potential options for doing so. Because I was creating custom fields to begin with, my solution of using custom attributes worked in that particular instance without an unreasonable amount of extra effort.

However, in a situation where a custom field would not be needed it would most likely be overkill to create one simply to apply a new custom attribute. Not to mention, I knew there had to be a more efficient way to retrieve the value from the date field textbox without having to cycle through the entire list of page controls. That’s when I came across the option of retrieving the form itself using WebUtil.GetParent<SimpleForm>(this); Once the form is retrieved, it’s just a matter of searching for the appropriate field and name.

 

Example code:

 

// WFFM form 
var form = WebUtil.GetParent<SimpleForm>(this); 
// WFFM field 
var dateField = (FutureDatePicker)WebUtil.FindFirstOrDefault(form, d => d is FutureDatePicker && (d as FutureDatePicker).Result.FieldName == "Call Date"); 
// Field Value 
if (dateField != null) { 
var inputtedDate = dateField.Text;   
}

Retrieve form and search for field

 

Solution Three:

 

As you can see, both solutions detailed so far involve the use of custom standard validation requiring that separate custom validation be created for both the FutureDatePicker and TimePickerDropList fields.  If separating this validation is not an option or perhaps you simply prefer to contain logic for validation of both fields together, another solution for cross-field validation would be to instead create a custom form verification action. This option allows access to all fields on the form and the creation of complex validation logic.

 

Example code:

 

var callDateControl = fields.FirstOrDefault<ControlResult>((ControlResult f) => f.FieldName == "Call Date");
var controlResult1 = fields.FirstOrDefault<ControlResult>((ControlResult f) => f.FieldName == "Call Time"); 

Retrieve form fields through verification action

 

Conclusion:

 

As demonstrated in the examples above, Sitecore developers have a few different options for validation across multiple fields within Web Forms for Marketers. I personally prefer the custom verification action solution as it can be applied on a form level and be developed to be dynamically used on multiple forms. Of course, each of these proposed options has its own set of advantages and limitations and in the world of coding there likely is a scenario for which each might be the preferred solution for one reason or another.

Here’s another post that may interest you: Accessing Contextual Data from a Custom Form Field Validator

Michelle Banzer

Developer
Tags
  • Development
  • Sitecore
  • Tutorial
  • Web Development

Recent Work

Check out what else we've been working on