Monday, April 7, 2014

Dynamic forms - with a keep it simple approach

Wouldn't it be cool if editors just could drag-and-drop input fields and rearrange the design of forms in EPiServer? I can almost hear you say: XForms. Yes, I've heard about it. I have written a fair amount of code using the good ol' built in EPiServer feature. I have even succeeded in taming the XForm Beast to obey the simple rules of MVC (with help from you great bloggers out there). But that is a different story.

What about the forms that need other features than the ones provided by EPiServer XForms?

An example scenario:
We want to collect user input by using a web based form. The input data should be passed on to a back end system for processing, and should not be stored in the EPiServer database at all. Additionally, the editors want to create different campaign pages with different forms. The editors want to A/B test the campaign pages - to learn how to reshape and adjust the form to maximize conversion rates. Can this be done with EPiServer?

Here's a simple solution: blocks.
(check out the full example at GitHub)

The Input Block
Let's write a block type that can be used in forms all over the site. Instances of the block type will behave in different ways, when used in different contexts. To succeed with that, each instance of the block type need to have really good answers to some existential questions:

What am I?

Where do I belong?

How should I act in public?

The answers come in the form of categories. It doesn't have to be like that, It's just me. I think categories are cool. They remind me of twitter hash tags (I think twitter hash tags are cool too).


What am I? (Field) Where do I belong? (Group) How should I act? (Behaviour)


This instance is a "first name" input field, existing in a "user" context,
with user input set to required.


The View
Now we have the answers to the deep questions asked earlier and can write the user interface. 


The example code has some convenience methods, 
such as generating the input field name based on the categories.


Html output generated with data from the model (the block type).
A server side regular expression pattern is also passed on to the client.


The Validation
What about validation? Dynamic forms are by nature quite unpredictable. But we know about the behaviour of the individual block, and can use that knowledge to write both custom client and server validation. In this example I am using the Zurb Foundation Abide framework to perform client script validation. 


Custom validator added to the Abide framework, 
specialized in validating first name input fields. 
The name of the validator is the value of the selected subcategory for "Field".

"David" is valid. Nice to know.


This example code has two types of server validation. First up is the data annotations defined in the model. Secondly (if necessary) there is also the ability to perform custom validation. What validator to use is determined by the block behaviour (the categories), just like selecting the client validator. Here is where specialized validation takes place, such as dependencies between individual fields in the posted form.



Form data posted to the controller action method.


Going through each item in the page content area,
find the appropriate validator and execute the validation method. 
The model is passed to the validator.


That's it.

Check out the example code at GitHub. To get up and running you need to create an EPiServer 7.5 database and (of course) configure the connectionstrings file.

I really want your feedback on this. 

Please post your comments here or contact me on Twitter.

6 comments:

Unknown said...

Nice, would there be an option with a non-javascript validation as well?

Even if most browsers supports validation there is also a sense of security where the backend has a validation as well.

David Vujic said...

Absolutely. The example code has server validation (check out the image above with the Visual Studio debugger running and the actual code at GitHub). The client script validation should be only for giving the user quick feedback. The security is all about server validation.

Unknown said...

Ah, sorry my bad :)

David Vujic said...

No worries!

Josef said...

Hi! Did you really get this working? I tried to implement it, but I get problems with BlockData cast to ICategorizable. Browsing the SDK I find that PageData implements this interface, but not BlockData.

Did I miss something here?

Regards, Josef

Josef said...

Since my last comment I managed to run the code and found out that the BlockData object in fact was a Castle Proxy object implementing the interface. Please either ignore my previous coment or keep it to avoid further confusion about this :-)