How do I link two dropdown fields in Episerver such that the values of a dropdown are based on the selected value of another?
Solution:
-
Use the existing Episerver form field “Selection” for the first dropdown
-
Expose a new dropdown field (i.e. “Secondary selection”) that would derive from the field above and introduce new properties
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:
And also on the Basic Form elements panel:
And author away!
If you want Episerver for your site, come to Niteco, the world’s largest Episerver Partner. We power your Episerver ambition.
to transform your business and drive results?