How to extend Episerver Forms

UPDATE: On the 31st July, Episerver released Form field dependencies, which is available on Episerver.Forms version 4.15.0. So if you got here because you’d like to have a dropdown that has dependencies on other elements, just update your Episerver.Forms package. Otherwise, if you’re here to see how you can extend Episerver Form fields, the below should be a good example.

How do I link two dropdown fields in Episerver such that the values of a dropdown are based on the selected value of another?

Dropdown values dependent on another

Solution:

  1. Use the existing Episerver form field “Selection” for the first dropdown
  2. Expose a new dropdown field (i.e. “Secondary selection”) that would derive from the field above and introduce new properties

Episerver secondary dropdown

The code

  1. Extend the OptionItem.cs Episerver class
    public class DependentOptionItem : OptionItem
    {
       [Display(
          Name = "Value relevant to",
          Order = 4000)]
       public virtual string ValueRelevantTo { get; set; }
    }
  2. Tell Episerver how to treat the new custom option item as as a property
    [PropertyDefinitionTypePlugIn]
    public class DependentOptionItemProperty : PropertyList<DependentOptionItem>
    {
       protected override DependentOptionItem ParseItem(string value)
       {
          return JsonConvert.DeserializeObject<DependentOptionItem>(value);
       }
    }
  3. Now, we can expose a new dropdown field, calling it “Secondary selection”. Things to note:
    • Derive from SelectionElementBlockBase (existing dropdown field)
    • GroupName & ordering has been set so it’s positioned next to the existing dropdown in the CMS
    • The ImageUrl has been set to make the authors happy
    • The “Items” property has been overriden to use our new DependentOptionItem above
    [ContentType(
       DisplayName = "Secondary Selection", 
       GUID = "d9f2477f-3d53-441a-b5eb-75eebb7106b7", 
       GroupName = "BasicElements",
       Description = "Used to display values that are dependent on another selection element's value", 
       Order = 2301)]
    [ImageUrl("/static/images/contenttypes/selectionelementblock.png")]
    public class SecondarySelectionElementBlock : SelectionElementBlockBase
    {
       [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor))]
       public override IEnumerable<DependentOptionItem> Items { get; set; }
    
       [Display(
          Name = "Dependent on field",
          Order = -3001)]
       [Required]
       [AllowedTypes(typeof(InputElementBlockBase))]
       public virtual ContentReference DependentOnField { get; set; }
    }
  4. It’s time to register a UIDescriptor for our new dropdown field
    [UIDescriptorRegistration]
    public class SecondarySelectionElementBlockDescriptor : FormElementBlockDescriptor<SecondarySelectionElementBlock>
    {
    }
  5. Add some CSS to make sure an icon gets rendered against our new dropdown field (feel free to use your own! I just copied the icon from the existing dropdown)
    .epi-forms-icon.epi-forms-secondaryselectionelementblock__icon {
        background: url(/static/gfx/formIcons16x16.png) 0px -64px no-repeat;
    }
  6. HTML time – this is pretty much a clone of the SelectionElementBlock.cshtml but utilising the two new properties:
    @model YourLegalFriend.Content.Forms.SecondarySelectionElementBlock
    
    @{
    	var formElement = Model.FormElement;
    	var labelText = Model.Label;
    	var placeholderText = Model.PlaceHolder;
    	var defaultOptionItemText = !string.IsNullOrWhiteSpace(placeholderText)
    		? placeholderText :
    		Html.Translate(string.Format("/episerver/forms/viewmode/selection/{0}", Model.AllowMultiSelect ? "selectoptions" : "selectanoption"));
    	var defaultOptionSelected = Model.Items.Count(x => x.Checked.HasValue && x.Checked.Value) <= 0 ? "selected=\"selected\"" : "";
    	var items = Model.GetItems();
    	var defaultValue = Model.GetDefaultValue();
    }
    
    @using (Html.BeginElement(Model, new { @class = "FormSecondarySelection FormSelection" + Model.GetValidationCssClasses(), data_f_type = "selection" }))
    {
    	<label for="@formElement.Guid" class="Form__Element__Caption">@labelText</label>
    	<select name="@formElement.ElementName" id="@formElement.Guid" @(Model.AllowMultiSelect ? "multiple" : "") @Model.AttributesString 
    			@RenderDependentField() disabled="disabled">
    		<option disabled="disabled" @defaultOptionSelected value="">@defaultOptionItemText</option>
    
    		@foreach (var item in items)
    		{
    			var defaultSelectedString = Model.GetDefaultSelectedString(item, defaultValue);
    			var selectedString = string.IsNullOrEmpty(defaultSelectedString) ? string.Empty : "selected";
    
    			<option value="@item.Value" @selectedString @defaultSelectedString data-f-datainput data-relevant-to="@item.ValueRelevantTo">@item.Caption</option>
    		}
    	</select>
    }
    
    @helper RenderDependentField()
    {
    	if (Model.DependentOnField != null)
    	{
    		@Html.Raw("data-dependent-on=\"" + Model.DependentOnField.GetContentGuid() + "\"")
    	}
    }
  7. Javascript time – this handles disabling the options that are not relevant to the selected value of the primary dropdown
    $('.EPiServerForms .FormSecondarySelection').each(function() {
       var $select = $(this).find('select');
       var $primarySelect = $('#' + $select.data("dependent-on"));
     
       // connect the primary select item to the secondary
       $primarySelect.change(function() {
          if ($select.is(':disabled')) {
             $select.prop('disabled', false);
          }
    
          // only display secondary options that are relevant to the primary value
          var primaryValue = $(this).val();
          $select.find('option').hide();
          $select.find('option[data-relevant-to="' + primaryValue + '"]').show();
       });
    });

Build and run the application…

You should now see two dropdowns:

Episerver secondary selection icon

And also on the Basic Form elements panel:

Episerver basic elements icon

And author away!

Two linked dropdowns

Tags: