Episerver – dynamically populate a second dropdown based on first

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: (Please note this post only applies to those who are using Episerver Forms.)

  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
          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
    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
       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)]
    public class SecondarySelectionElementBlock : SelectionElementBlockBase
       [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor))]
       public override IEnumerable<DependentOptionItem> Items { get; set; }
          Name = "Dependent on field",
          Order = -3001)]
       public virtual ContentReference DependentOnField { get; set; }
  4. It’s time to register a UIDescriptor for our new dropdown field
    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>
    @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[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