Asynchronous web forms with Episerver XForms
The world of doing customizable forms has recently changed with the introduction of Episerver Forms as an add-on. Myself and my developer colleagues are pleased with this new product and are for once looking forward to implementing the customizable forms on client websites.
Even though this new product is a big improvement, it does not mean that its bigger brother – XForms – are out for good. We are unfortunately still faced with the challenge of implementing the older XForms product due to it being a dependency to e.g. Episerver Connect for Marketing Automation products.
XForms has numerous times, since its introduction along with Episerver CMS 5, proven to assist the business with creating the flexibility needed when working with web forms. The fact that the product is old is pretty obvious. Numerous blog posts, by fellow Episerver developers, has covered how XForms can be more future proofed with support of HTML5 and field level validation. No one, as far as I am aware, tried to take it even further by introducing asynchronous request management. The wish, prior to this implementation, was to preserve the current state of the page without having to do anything.
XForms was, as part of Episerver CMS 7, upgraded to natively be supported by ASP.NET MVC. We can, by levering and extending the existing MVC AJAX form management (built on jQuery Unobtrusive), get to our goal in a series of simple steps.
Extend the AjaxHelper to support XForms.
We need to extend the existing AjaxHelper to support our need to build a jQuery Unobtrusive compatible XForm.
Above snippet makes sure that a compatible <form/> tag is rendered with the necessary data-* attributes required by the client side framework. Most importantly, this snippet enables us, as developers, to set a target URL that supports the necessary XForm parameters ensuring that the request can be managed correctly.
Create Model, View and Controller
Our AjaxHelper extension is intended to be used in the view responsible of rendering our form. Below code should be intuitive if you already feel comfortable with Razor. It is important that the AjaxOptions reflects the behavior we need. My example uses jQuery Unobtrusive to replace the existing markup, inside the div with id form-xyz, with new markup from the response to our asynchronous request.
Another important detail is the ActionUrl property on our view-model. It sets the URL of the Controller that XForms is supposed to request when a user clicks the submit button.
We tend to generate this ActionUrl based on native MVC routing. The ActionUrl is, as shown in the snippet, generated based on a route pointing to an Index action on a seperate FormController. It is highly recommended to include a language route value in order to enable the native Episerver support for localization.
Please be aware that the logic responsible of building our view-model has been centralized to a class called FormViewModelBuilder. It is done because our implementation later requires this separate FormController to re-build the same exact view-model. It can be avoided if you, in contrast to our example, are using XForms directly on a PageType.
Below MVC Controller, who renders all instances of our FormBlockType, ties it all together and enables us to render an XForm with support for jQuery Unobtrusive.
Only thing left is to create the FormController that manages XForm compatible POST requests.
Manage form submissions
Below FormController* is not very different from what I normally see when developers implement XForms.
Our Index action receives the actual submit and dispatches this to the native XForm handler within Episerver. We will then, based on the input typed by the user, either get redirected to our Success or Failed action. Please be aware that the name of these actions depends on the submit URL that were built previously.
Biggest difference, compared to ordinary implementations, is the fact that our Success and Failed action returns a PartialView. jQuery Unobtrusive will, due to our AjaxOptions, use the returned markup as the new content and thereby updating the user with the state of the submit.
Implementing XForms often proves to be a challenge. I’ve though previously helped myself and my team via a similar approach to above implementation. It makes it way easier to manage the never ending ModelState and ViewData issues when e.g. having to implement XForms via an independent BlockType. It also helps tremendously when dealing with page state and the fact that frontend developers tend simplify management of this via asynchronous requests.
I would like to emphasize that above example has been taken out of a client context and are, in reality, more complicated. Previously this has successfully been combined with Ove Andersen’s suggestion on how to customize XForm rendering. It creates full HTML5 compatibility and field-level validation.