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

08 November 2018

2 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...

5 min read

Episerver Personalization with persistent anonymous profiles

Episerver Personalization is a powerful feature to ensure your website displays relevant content to your visitors. At Luminary we needed a way to persist an anonymous visitor's personalisation profile across sessions without using Episerver Insight or custom cookies. Here's how we did it...

Darren Stahlhut
Darren Stahlhut

12 October 2018

5 minute read

Luminary team at DDD

4 min read

Providing a platform for our platforms

As the year draws to a close, we take a look back over some of the industry events that we got involved with in 2019.

Tami Iseli
Tami Iseli

04 December 2019

4 minute read

Greg Baxter, Andy Thompson and Darren Stahlhut

2 min read

Luminary secures MVP trifecta

In an unprecedented industry coup, Luminary has had three of its team members awarded MVP status across three different digital experience platforms – Kentico EMS, Sitecore and Episerver.

Tami Iseli
Tami Iseli

07 February 2020

2 minute read

Luminary is the APAC Solution Partner of the Year for Optimizely (formerly Episerver).

Ynze Nunnink

Ynze Nunnink

Senior Developer/Optimizely MVP

Ynze is an experienced developer who specialises in .NET and Optimizely.

4 min read

Generating image metadata and SVG thumbnails with Episerver DXC Azure Blob storage

Episerver has an ImageData base class that natively supports images. But we needed to extend it to provide metadata fields and display SVG thumbnails. Here's how we did it...

Darren Stahlhut
Darren Stahlhut

30 July 2018

4 minute read

Episerver Award Presentation

1 min read

Luminary wins Episerver Partner Award

Luminary has been recognised as Episerver's most valuable new partner in the Asia Pacific region.

Tami Iseli
Tami Iseli

13 March 2018

1 minute read

Darren Stahlhut

1 min read

Luminary developer named Episerver MVP

Luminary Tech Lead Darren Stahlhut has been awarded Episerver Most Valued Professional status.

Tami Iseli
Tami Iseli

31 January 2020

1 minute read

Keep Reading

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