Customising Episerver Forms

Episerver Forms has a lot of built-in functionality, but in some cases it is necessary to extend that functionality, which can be a tricky thing to do. This post contains information about rendering, overriding and problem solving custom Episerver form functionality.

Ynze Nunnink

By Ynze Nunnink, 8 November 20182 minute read

There are a couple of important factors about how Episerver renders its forms that can be difficult to understand. In this post I will give some examples of how to implement your own views and functionality.

Rendering client resources

Episerver Forms uses client resources for the CSS and Javascript. They are registered in the FormContainerBlock controller. Be sure to call the index method when using your own custom template. The CSS is not required and can be disabled in the `_protected/EPiServer.Forms/Forms.config` configuration file.

injectFormOwnStylesheet="true"

The Javascript is required for the asynchronous post functionality of the form. For this to work, make sure the client resources get rendered in the header and footer on every page. In the example below you can see where and how to render them.

@using EPiServer.Framework.Web
@using EPiServer.Framework.Web.Mvc.Html

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title></title>

    @Html.RequiredClientResources(RenderingTags.Header)
</head>
<body>
    <div>
        @RenderBody()
    </div>

    @Html.RequiredClientResources(RenderingTags.Footer)
</body>
</html>

The client resources need to be registered before they get rendered. The registering for the forms happens in the FormContainerBlockController. When the index is called, it registers the required resources. In some cases this does not happen, for example when the form is rendered asynchronously. In that case it can also be registered manually by using the following code:

var formController = new EPiServer.Forms.Controllers.FormContainerBlockController();
formController.RegisterCssResources();
formController.RegisterScriptResources(new FormContainerBlock());

Rendering a custom form template

In the examples below, the form gets wrapped in a modal. The wrapper contains the css classes needed for the the form to have the correct styling in our form block preview. First off we ran into the following issue:

Viewbag is null error

Server Error in '/' Application.
Cannot convert null to 'bool' because it is a non-nullable value type
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
Exception Details: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'bool' because it is a non-nullable value type

This error happens when you try to render a custom form but the FormContainerBlockController is not being called. A solution for this error is renaming your view and identifying it from the custom form controller. Below is the code we used.

[TemplateDescriptor(AvailableWithoutTag = true, Default = true, ModelType = typeof(ContactFormBlock), TemplateTypeCategory = TemplateTypeCategories.MvcPartialController)]
public class ContactFormBlockController: FormContainerBlockController
{
    public override ActionResult Index(FormContainerBlock currentBlock)
    {
        //Call base index for the Client Resource registration but not the view
        //Not needed in case you manually render them in base controller
        base.Index(currentBlock);

        return PartialView("_ContactFormBlock", currentBlock);
    }
}

ContactFormBlock.cs

[ContentType(DisplayName = "Contact Form", GUID = "DD088FD8-895E-47EF-9497-5B7A6700F4A6", GroupName = EPiServer.Forms.Constants.FormElementGroup_Container, Order = 4000)]
[ServiceConfiguration(typeof(IFormContainerBlock))]
public class ContactFormBlock : FormContainerBlock
{

}

The view for this form is quite simple. It has some html elements for a modal and then renders the actual form inside. This way the contact form will have its own styling but doesn't change anything about the form itself. Later in this post I'll explain how to customise the form. Note that the view's name has the "_" prefix. 

/Views/Shared/Blocks/_ContactFormBlock.cshtml

@model ContactFormBlock

@{
    var hideModal = string.Empty;

    //hide the modal when it's not in edit or preview mode
    if (RequestSegmentContext.CurrentContextMode == ContextMode.Default)
    {
        hideModal = "hidden";
    }
}

<div class="modal-content" @hideModal data-id="@Model.Form.FormGuid">
    <h3>@Html.PropertyFor(model => model.Title)</h3>
    <div class="contact-form">
        @Html.Partial("/Episerver/EPiServer.Forms/Views/ElementBlocks/FormContainerBlock.ascx", Model)
    </div>
</div>

Customising existing Form views

It is possible to override the FormContainerBlock or any of the existing form elements in case you require more control over them. All you need to do is put a view with the same name in your blocks folder. The FormContainer is quite complex but luckily I found this gist on github that contains the MVC code for FormContainerBlock.cshtml:

https://gist.github.com/JohanPetersson/ec143f4d0178253cb2bf44e40ef8478f

Keep in mind that when using a custom FormContainerBlock that you don't remove the classes from the markup, the form's asynchronous posting Javascript depends on these classes. For example, you can't easily get around using steps in your form, if you don't render the steps it can break the async javascript form submit.

Form Elements

Overriding an existing element is just as simple. The only thing you need to do is put a view with the same name as the element in the ElementBlocks folder. You can also change the path that Episerver looks for when providing custom form elements in the Forms.config

formElementViewsFolder="~/Views/Shared/ElementBlocks" 


Liked this post?

Here are some other content items you may find interesting...

Keep Reading

Want more? Here are some other blog posts you might be interested in.