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

epi-dropdowns

 

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-forms-extend

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-forms-extend

And also on the Basic Form elements panel:

episerver-forms-extend

And author away!

episerver-forms-extend

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

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?