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 menus for selecting primary and secondary food categories

 

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

 

Dropdown for Primary food category dependency with item list and relevant values

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:

New form element selection options including URL, secondary selection, and choices

And also on the Basic Form elements panel:

Form Elements panel showing selection and multiple or single choice options

And author away!

Dependent dropdowns form with primary and secondary food category selection options

If you want Episerver for your site, come to Niteco, the world’s largest Episerver Partner. We power your Episerver ambition.

Link copied!
Looking for a partner
to transform your business and drive results?
Let's Get In Touch
Looking for a partner<br/>to transform your business and drive results?