📋 Content Models
Optimizely Forms
📖 Docs

Optimizely Forms - Optimizely CMS 12

Exam Area: Content Area 5 – Content Models (25%)
Reference: https://docs.developers.optimizely.com/content-management-system/docs/optimizely-forms


1. What Is Optimizely Forms?

Optimizely Forms is an add-on that lets editors create forms through a drag-and-drop UI without writing code. Forms can:


2. Installation

# NuGet package
dotnet add package EPiServer.Forms
// Program.cs
builder.Services.AddOptimizelyForms();

// or in IConfigurableModule
services.AddOptimizelyForms(options =>
{
    options.MaxFileSizeInBytes = 1024 * 1024; // 1MB
});

3. Form Structure

FormContainerBlock (root)
├── Step 1 (FormStepBlock)
│   ├── TextboxElementBlock (Name)
│   ├── TextboxElementBlock (Email)
│   └── TextareaElementBlock (Message)
├── Step 2 (FormStepBlock)
│   ├── FileUploadElementBlock
│   └── CheckboxElementBlock (GDPR consent)
└── Submit (SubmitButtonElementBlock)

4. Built-in Form Elements

ElementTypeDescription
TextboxElementBlockText inputShort text field
TextareaElementBlockTextareaMulti-line text
DropdownElementBlockDropdownSingle select
CheckboxElementBlockCheckboxBoolean
ChoiceElementBlockRadio/Checkbox groupMultiple options
FileUploadElementBlockFile uploadUpload files
HiddenElementBlockHidden fieldHidden value
NumberElementBlockNumber inputNumeric
DateTimeElementBlockDate pickerDate/time
SelectionElementBlockSelect listFrom predefined options
SubmitButtonElementBlockSubmit buttonSubmit form
FormStepBlockStep containerMulti-step form
FormContainerBlockForm rootContains all elements

5. Rendering Forms in Views

@* In page view *@
@using EPiServer.Forms.Helpers.Internal

@if (Model.CurrentPage.ContactForm != null)
{
    @Html.PropertyFor(m => m.CurrentPage.ContactForm)
}

@* Or render directly *@
@{
    var formContent = _contentLoader.Get<FormContainerBlock>(formReference);
}
@Html.RenderEPiServerForms(formContent, ViewContext)

6. Form Submission Handling

// IFormDataRepository - reads form submissions
public class FormDataService
{
    private readonly IFormDataRepository _formDataRepository;

    public FormDataService(IFormDataRepository formDataRepository)
    {
        _formDataRepository = formDataRepository;
    }

    public IEnumerable<FormIdentity> GetFormSubmissions(Guid formGuid)
    {
        return _formDataRepository.GetSubmissions(formGuid);
    }
}

7. Custom Form Element

// Create custom form element
[ContentType(
    DisplayName = "Rating Element",
    GUID = "...",
    GroupName = "Form Elements")]
public class RatingElementBlock : ElementBlockBase
{
    [Display(Name = "Max rating")]
    [Range(1, 10)]
    public virtual int MaxRating { get; set; }

    [Display(Name = "Label")]
    public virtual string Label { get; set; }
    
    // ElementName is required for form submission
    public override string Label => this.LocalLabel;
}

8. Form Events

// Subscribe to form submission events
[InitializableModule]
public class FormEventHandler : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        FormsEvents.Instance.FormsSubmitting += OnFormsSubmitting;
        FormsEvents.Instance.FormsSubmissionFinalized += OnFormsSubmitted;
    }

    public void Uninitialize(InitializationEngine context)
    {
        FormsEvents.Instance.FormsSubmitting -= OnFormsSubmitting;
        FormsEvents.Instance.FormsSubmissionFinalized -= OnFormsSubmitted;
    }

    private void OnFormsSubmitting(object sender, FormsSubmittingEventArgs e)
    {
        // Before submit - can cancel
        var formData = e.SubmissionData;
        
        // Validate custom logic
        if (!ValidateSubmission(formData))
        {
            e.CancelAction = true;
            e.CancelReason = "Validation failed";
        }
    }

    private void OnFormsSubmitted(object sender, FormsSubmittedEventArgs e)
    {
        // After submit - integrate with external systems
        var submissionId = e.SubmissionId;
        ExternalCRM.SendLead(e.SubmissionData);
    }
}

9. Form Actors (Post-Submit Actions)

// Form Actor - runs after submit
public class CrmIntegrationActor : IFormActor
{
    public string Key => "CrmIntegration";
    public string DisplayName => "Send to CRM";
    
    public Task<object> RunAsync(IFormActorData formActorData)
    {
        var email = formActorData.SubmissionData
            .FieldData.FirstOrDefault(f => f.Name == "Email")?.Value;
        
        // Send to CRM
        CrmService.CreateLead(email);
        
        return Task.FromResult<object>(null);
    }
}

Review Questions

  1. Which NuGet package is needed for Forms? (EPiServer.Forms)
  2. What is FormContainerBlock? (The root block containing the entire form and its steps)
  3. Where do editors create forms? (In the CMS Edit View, dragging and dropping elements into a FormContainerBlock)
  4. What is a Form Actor used for? (Runs actions after form submission – e.g., sending email, syncing CRM)
  5. What does IFormDataRepository do? (Reads/queries form submissions)