Skip to content

Creating Episerver Forms via the API

Introduction to Episerver Forms

Utilizing forms to collect information from visitors in a streamlined, flexible manner is a core component for most websites (e.g., “Contact Us” form). Similarly, easily creating and maintaining these forms without interaction from the development team, as well as viewing submitted form data are equally important aspects of a modern website. While Episerver does not supply this functionality out of the box, the Episerver Forms add-on satisfies each of these requirements.

The Forms add-on is free, and can be installed via Nuget:

Install -Package EpiServer.Forms

It allows editors to create custom forms using a variety of built-in form elements, and is also extensible through custom elements. These forms, and their elements, are based on the concept of blocks, which means they can then be used throughout a site, just like any other block. Form submissions can easily be viewed and sorted via the Episerver backend. Additionally, it’s possible to configure post-submission emails, as well as custom “WebHook actors,” which can be used to facilitate third-party integrations.

Okay…So, if it’s Flexible and Easy, What’s the Problem?

Functionally speaking, Episerver Forms works well, and being based on the concept of blocks means that it offers editors an easy to use, familiar method to create custom forms. Overall, the editor experience is good – including the user guide.

However, things aren’t as clear from a development perspective; this became apparent during a recent project when attempting to create forms via code rather than the backend. The developer documentation is not as robust as the editor-focused user guide, which means doing CRUD operations involves some detective work (via decompiled DLLs), along with some good, old-fashioned trial and error.

Let's get under the hood of Episerver Forms!

Let’s get under the hood of Episerver Forms!

IContentRepository to the Rescue!

As mentioned, Episerver Forms are block-based, which means they can be created and modified the same way as any other piece of IContent – the ubiquitous IContentRepository saves the day! This interface provides basic CRUD operations for anything that implements IContent, and is likely already familiar to anyone that has worked with the Episerver API.

An Episerver Form is essentially a block that contains several properties, including a ContentArea that holds a collection of form elements. The form itself inherits from FormContainerBlock, while each form element inherits from ElementBlockBase. These classes are not well documented, but are reasonably easy to understand. As a side note: it is possible to extend these classes with custom implementations (as well as using a custom view), but the default types will be used in the following examples.

Anyway, enough background information – without further ado, some let’s get into some code.

Creating a New Form

To create a new form via the API:

//get an instance of the content repository - NOTE: this should usually come from constructor injection
var contentRepository = ServiceLocator . Current . GetInstance ( ) ;

//get the root forms folder - NOTE: the folder name ("Episerver Forms") does not appear to be documented, and neither is the location of the corresponding constant
var formsRootContentFolder = contentRepository . GetChildren (ContentReference . GlobalBlockFolder ) . FirstOrDefault (x =&gt ; x . Name == EPiServer . Forms . Configuration . Settings . RootFolderName ) ;

//create a new form in that folder (or a sub-folder, if desired)
var form = contentRepository . GetDefault (formsRootContentFolder . ContentLink ) ;

( (IContent )form ) . Name = "Form Name" ;

form . Title = "Form Title" ;
form . Description = "Form's description." ;
form . SubmitSuccessMessage = "Form submitted successfully." ;
form . AllowAnonymousSubmission = true ;
form . AllowExposingDataFeeds = true ;
form . AllowMultipleSubmission = true ;
form . AllowToStoreSubmissionData = true ;

//save the form
contentRepository . Save ( (IContent )form, SaveAction . Publish, AccessLevel . NoAccess ) ;

This is a relatively standard example of using IContentRepository to create a new piece of IContent. The only differences are a few form-specific properties:

  • Title: displayed at the top of the form
  • Description: displayed at the top of a form, just the Title
  • SubmitSuccessMessage: displayed to visitor after a successful form submission
  • AllowAnonymousSubmission : toggles whether users can submit the form without being logged in
  • AllowExposingDataFeeds: toggles whether form submissions are available via the Service API
  • AllowMultipleSubmission: toggles whether users are allowed to submit a form multiple times
  • AllowToStoreSubmissionData: toggles whether form submission are stored in the Episerver database

Once a form has been created, form elements can be added to the ElementsArea property. However, before elements can be added to this ContentArea, they must first be created.

Creating a New Form Element

To create a new form element via the API:

//get an instance of the content asset helper
//NOTE: this should usually come from constructor injection
var contentAssetHelper = ServiceLocator . Current . GetInstance ( ) ;

//get the form's content asset folder
//NOTE: by default, all elements for a given form are created in the form's asset folder, but it may be possible to store them elsewhere to facilitate sharing elements across forms
var formAssetFolder = contentAssetHelper . GetOrCreateAssetFolder ( ( (IContent )form ) . ContentLink ) ;

//create a new form element in that folder
var formElement = contentRepository . GetDefault (formAssetFolder . ContentLink ) ;

( (IContent )formElement ) . Name = "Form Element Name" ; //this is required - used when rendering form submissions

formElement . Description = "Form element's description" ;
formElement . Label = "Form Element Label" ;
formElement . PlaceHolder = "Form Element Placeholder" ;
formElement . Validators = typeof (EPiServer . Forms . Implementation . Validation . RequiredValidator ) . FullName ;

//save the form element
contentRepository . Save ( (IContent )formElement, SaveAction . Publish, AccessLevel . NoAccess ) ;

Again, this is relatively straightforward, but not well documented. The element-specific properties are as follows:

  • Description: displayed as a tooltip
  • Label: displayed beside the input element
  • Placeholder: displayed as a watermark/placeholder in the input element
  • Validators: used to determine how to validate the element’s value (see “Element Validation” below)

One thing to note is that the form elements get created in the form’s asset folder (“For This Page/Block” in the backend), which makes them available to only that form. It may be possible to create form elements elsewhere, and share them across multiple forms, but this has not been tested. Also, note that the IContent.Name property is required — it is used to render the column header when viewing the form submission data via the Episerver backend.

There are a variety of built-in form element types that cover the most common input types, and custom elements can also be created for use cases that are not covered. The built-in element types are named in a self-explanatory manner (e.g., TextboxElementBlock), and exist in the EpiServer.Forms.Implementation.Elements namespace.

Element Validation

As noted above, the Validators property on the form element block is responsible for element value validation. This property is a string, and contains a delimited collection of fully-namespaced validator types. This delimiter is not documented, but some testing reveals that it’s a triple pipe (“|||”). There also appears to be a constant for this value at EPiServer.Forms.Constants.RecordSeparator.

For example, to set the Validators property for a required email form element:

//get the validator type names and join them with the record separator
var validators = new string [ ] { typeof (EPiServer . Forms . Implementation . Validation . RequiredValidator ) . FullName, typeof (EPiServer . Forms . Implementation . Validation . EmailValidator ) . FullName } ;
formElement . Validators = string . Join (EPiServer . Forms . Constants . RecordSeparator, validators ) ;

//save the form element
contentRepository . Save ( (IContent )formElement, SaveAction . Publish, AccessLevel . NoAccess ) ;

The built-in validators exist in the EpiServer.Forms.Implementation.Validation namespace and, like the built-in form elements, are named in a self-explanatory manner (e.g., RequiredValidator). It’s also possible to create custom validators. The official documentation provides and example of how to leverage a custom validator.

Adding Elements to a Form

To add an existing element to an existing form:

//initialize the form's content area and add the element
form . ElementsArea = form . ElementsArea ?? new ContentArea ( ) ;
form . ElementsArea . Items . Add ( new ContentAreaItem { ContentLink = ( (IContent )formElement ) . ContentLink } ) ;

//save the form
contentRepository . Save ( (IContent )form, SaveAction . Publish, AccessLevel . NoAccess ) ;

No surprises here – just add the element and save the form!

Final Thoughts

Offering visitors a method to submit data via forms is is a core component in a modern website. This collection of visitor data, which can range from a simple “Contact Us” form to complex, multi-step forms, provides a direct channel to potential customers. Overall, the Episerver Forms add-on offers a flexible, easy to use method for editors to facilitate this data collection without involvement from the development team.

Though the editor experience is relatively streamlined and well-documented, developers may need to do some investigative work in order to build and maintain forms via the API. The developer documentation contains an overview of the functionality offered by Forms, but the lack of code-based examples makes interacting with the API somewhat difficult. For example, the specifics in this post (e.g., EPiServer.Forms.Constants.RecordSeparator) were discovered through some quality time with dotPeek.

Did I miss something? Probably. Let me know in the comments!