How to extend Episerver Forms

How to extend Episerver Forms

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:

  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

 

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.

 

About The Author

Nicola Ayan

Nicola Ayan

Episerver Solution Architect

Nicola Ayan is Niteco’s new Sydney-based Solutions Architect whose knowledge of Episerver has won her a spot as a MVP. In addition to her Episerver expertise, Nicola is also Microsoft certified. She’s an avid blogger on all-things Episerver and Microsoft. Contact her to discuss your next project: [email protected]

Contact us

close