Token Replacement on Sitecore Experience Forms

Sitecore Experience Forms are new to Sitecore 9. This module is a built-from-the-ground-up replacement for the old Web Forms for Marketers and it’s pretty powerful. One of its features is multi-page forms. Multi-page forms means you can segment your form via next and previous buttons. One thing that isn’t really clear is how to pass data from one page to another.

I wanted to figure out how to pass data across pages. Here’s the scenario:

  • Page 1 has a “First Name” single-line input field
  • Page 1 has a “next” button
  • Clicking the “next” button brings the user to Page 2
  • Page 2 has a header (text field) that says “Thanks, {{First Name}}

It doesn’t seem like there was an easy way to get this information. I posed the question on Stack Exchange but nobody seemed to have an answer, so I became determined to figure this out myself.

After doing some reflection with dotPeek, I was able to determine that view models all go through the <forms.getModel> pipeline. I explored a few other options, including overriding the FormBuilderController, but determined this pipeline was the cleanest way to go. Going this route, I created a pipeline processor that determines if the passed in model was a TextViewModel, which is the type used on Text fields. If it is, the processor will replace instances of {{First Name}} with the value grabbed from the First Name input field on page 1.

Let’s take a look at some code:

public class ReplaceFirstNameToken : MvcPipelineProcessor<GetModelEventArgs>
{
    private readonly IFormRenderingContext _formRenderingContext;
 
    /// <summary>
    /// ID of the Field on the Form
    /// </summary>
    public string FieldId { get; set; }
    public ReplaceFirstNameToken(IFormRenderingContext renderingContext)
    {
        _formRenderingContext = renderingContext;
    }
 
    public override void Process(GetModelEventArgs args)
    {
        // Check to make sure the Field ID has been set
        if (string.IsNullOrEmpty(FieldId))
        {
            Log.Error("ReplaceFirstNameToken: Parameter cannot be null or empty: FieldId", this);
            return;
        }
 
        // This token replacement is only valid on form fields that inherit from TextViewModel (e.g. the "Text" field type)
        if (!(args.ViewModel is TextViewModel textViewModel)) return;
 
        ReplaceTokensIfApplicable(textViewModel);
    }
 
    protected virtual void ReplaceTokensIfApplicable(TextViewModel textViewModel)
    {
        // If the FieldID isn't a valid ID, don't do anything
        if (!ID.TryParse(FieldId, out ID fieldId)) return;
         
        // Get the value of the first name field
        var textField = _formRenderingContext.GetPostedField(fieldId) as StringInputViewModel;
        string firstName = textField == null ? string.Empty : textField.Value;
 
        // Replace the FirstName token in the Text of the textViewModel
        textViewModel.Text = textViewModel.Text.Replace("{{FirstName}}", firstName);
    }
}

And the corresponding config patch:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <forms.getModel>
        <processor type="MyProject.Foundation.Form.Pipelines.Forms.GetModel.ReplaceFirstNameToken, MyProject.Foundation.Form" patch:after="processor[@type='Sitecore.ExperienceForms.Mvc.Pipelines.GetModel.CreateModel, Sitecore.ExperienceForms.Mvc']" resolve="true">
          <!-- The ID of the Input field itself on the form -->
          <FieldId>{10F6D000-C0B9-4053-9039-D97778D1E1C8}</FieldId>
        </processor>
      </forms.getModel>
    </pipelines>
  </sitecore>
</configuration>

Voila! I have now signed up for the mailing list to be notified when I can become Akshay’s friend!