Configuring editing component state in builder configuration dialogs

When defining editable properties for page or form builder components (widgets, page templates, personalization conditions, form field validation rules, etc.), you can use the API to configure the state of the assigned editing component. A typical use case is creating dynamic data sources for selector components such as DropDownComponent or RadioButtonsComponent (demonstrated in the examples on this page). This configuration is complementary to setting the component’s properties, visibility conditions, and validation rules via the corresponding attribute notation, and is intendedfor cases where statically configuring component values via attributes is not sufficient. 

Component state is configured via FormComponentConfigurator classes that can run custom code at specific points in an editing component’s lifecycle. Configurators allow you to modify all aspects of the component: properties, assigned visibility conditions, validation rules, and default value.

Configurator classes must:

  1. Inherit from the FormComponentConfigurator<TFormComponent> base class.
  2. Target a specific form component type via the generic TFormComponent parameter of the FormComponentConfigurator<TFormComponent> base class. For implementing types of out-of-the-box components, see Reference - System form components.
  3. Implement the Configure(FormComponent, IFormFieldValueProvider) method. The FormComponent parameter contains the instance of the configured component, giving you access to all its properties. IFormFieldValueProvider allows you to access the values of properties in the given dialog that precede the component being configured (according to the Order property that can be set when assigning editing components).

Assign configurators via the EditingComponentConfiguration attribute. The attribute has two constructors:

  • EditingComponentConfiguration(configuratorType) – used to assign a configurator that does not have any dependencies on the values of other components in the dialog. This configurator is invoked during the initial load of the configuration dialog. See Setting the initial component state.
  • EditingComponentConfiguration(configuratorType, fieldDependency) – used to assign a configurator with a dependency on the values of other components in the dialog. This configurator is invoked during the initial load of the configuration dialog and every time the value of the field designated as the dependency changes. See Establishing dependencies between fields.

Note: You can assign at most one component configurator per editing component.

Setting the initial component state

Configurators assigned via EditingComponentConfiguration(configuratorType) are invoked only on the initial load of the configuration dialog.

To assign a configurator to a property:

  1. Edit the class containing the component’s properties (e.g., a widget properties model class).
  2. Identify the property that you want to configure.
  3. Decorate the property with the EditingComponentConfiguration attribute (Kentico.Forms.Web.Mvc namespace).
  4. Set the following parameters:
    • configuratorType – the class that implements the component configurator you want to use. Use the typeof operator.

The example below shows a sample implementation of a component configurator and its usage.

Example

The following example creates a CountryConfigurator for DropDownComponent that serves as a data source for its options:

Example - country configurator



using System;
using System.Linq;

using CMS.Globalization;

using Kentico.Forms.Web.Mvc;  

// When assigned to a property with 'DropDownComponent' editing component, populates the component's options with all countries stored in the database
public class CountryConfigurator : FormComponentConfigurator<DropDownComponent>
{
    private readonly ICountryInfoProvider countryProvider;

    // Obtains required dependencies using constructor injection
    public CountryConfigurator(ICountryInfoProvider countryProvider)
    {
        this.countryProvider = countryProvider;
    }

    public override void Configure(DropDownComponent formComponent, IFormFieldValueProvider formFieldValueProvider)
    {
        // Retrieves all countries from the database and transforms them to the drop-down data format
        var countries = countryProvider.Get()
                                         .GetEnumerableTypedResult()
                                         .Select(c => c.CountryID + ";" + c.CountryDisplayName);

        // Sets the initial data of the component to the retrieved countries
        formComponent.Properties.DataSource = String.Join("\r\n", countries);
    }
}


The following code assigns the configurator to a DropDownComponent in a widget configuration dialog.

Example - country configurator usage



using Kentico.Forms.Web.Mvc;
using Kentico.PageBuilder.Web.Mvc;

public class MyWidgetProperties : IWidgetProperties
{
    [EditingComponent(DropDownComponent.IDENTIFIER, Label = "Country", Order = 3, Tooltip = "Select your country")]
    [EditingComponentConfiguration(typeof(CountryConfigurator))]
    public string Country { get; set; }

    ...
}


When the widget properties dialog is opened, the configurator ensures all countries are loaded and selectable from the Country property. In this example, CountryConfigurator.Configure is only triggered on the initial load of the dialog (the configurator does not have a field dependency assigned).

Establishing dependencies between fields within configuration dialogs

Form component configurators can be used to establish dependencies between fields within a dialog. A typical use case is a selector component that changes its available selection options based on the value of another field.  

For fields with dependencies, the configurator is called during the initial load of the configuration dialog, and every time the value of the field marked as a dependency changes.

Property order requirement

Value dependencies between properties are restricted by the order in which the properties are displayed in the configuration dialog. A dependency can only be established with properties of a lower order (controlled by the Order property that can be set when assigning editing components).

To assign a configurator with field value dependencies to a property:

  1. Edit the class containing the component’s properties (e.g., a widget properties model class).
  2. Identify the property that you want to configure.
  3. Decorate the property with the EditingComponentConfiguration attribute (Kentico.Forms.Web.Mvc namespace).
  4. Set the following parameters:
    1. configuratorType – the class that implements the component configurator you want to use. Use the typeof operator.
    2. dependencyFieldName – the name of the property whose change in value causes the configurator to reconfigure the component (call the Configure method again). Use the nameof operator.

The example below shows a sample implementation of a component configurator and its usage.

Example

The following example builds on the country selector example. It shows how to create a state selector that displays a collection of states for the selected country.

The configurator takes the country selector as a dependency. Every time a user selects a different country, it fetches the corresponding states from the database. If the selected country is not subdivided into states, the configurator hides the field instead.

Example - state configurator



using System;
using System.Linq;

using CMS.Globalization;

using Kentico.Forms.Web.Mvc;

// Configurator for components of the 'DropDownComponent' type that populates the dropdown with 
// a selection of all states based on the country selected in another field
public class StateConfigurator : FormComponentConfigurator<DropDownComponent>
{
    private readonly IStateInfoProvider stateInfoProvider;

    // Obtains required dependencies using constructor injection
    public StateConfigurator(IStateInfoProvider stateInfoProvider)
    {
        this.stateInfoProvider = stateInfoProvider;
    }

    public override void Configure(DropDownComponent formComponent, IFormFieldValueProvider formFieldValueProvider)
    {        
        // Gets the identifier of the country selected in the field marked as a dependency
        var countryId = GetCountryId(formFieldValueProvider);

        // If no country is selected (typically on initial page load), hides the state selector
        if (!countryId.HasValue)
        {
            HideField(formComponent);
            return;
        }

        // Gets all states for a given country
        var states = stateInfoProvider.Get().WhereEquals("CountryID", countryId).GetEnumerableTypedResult().Select(s => s.StateID + ";" + s.StateDisplayName);
        if (!states.Any())
        {
            // Hides the state selector component if subdivision into states doesn't exist for the selected country
            HideField(formComponent);
        }
        else
        {
            // Populates the state selector with states that belong to the selected country
            formComponent.Properties.DataSource = String.Join("\r\n", states);
        }
    }

    private int? GetCountryId(IFormFieldValueProvider formFieldValueProvider)
    {
        // The DependencyFieldName property of the base class contains the name
        // of the property set by the EditingComponentConfiguration attribute
        if (formFieldValueProvider.TryGet<string>(DependencyFieldName, out var country))
        {
            if (!String.IsNullOrEmpty(country))
            {
                // The format of individual options in the dropdown is: <value>;<DisplayName>
                // Ensured in the Country selector example
                return Int32.Parse(country.Split(";")[0]);
            }
        }
        return null;
    }

    private void HideField(DropDownComponent formComponent)
    {
        formComponent.Properties.VisibilityConditionConfiguration = new VisibilityConditionConfiguration
        {
            VisibilityCondition = new NeverVisibleVisibilityCondition()
        };
    }
}


The following code assigns the configurator to a DropDownComponent with a dependency on the value of another property: 

Example - country and state selector usage



using Kentico.Forms.Web.Mvc;
using Kentico.PageBuilder.Web.Mvc;

public class MyWidgetProperties : IWidgetProperties
{
    [EditingComponent(DropDownComponent.IDENTIFIER, Label = "Country", Order = 3, Tooltip = "Select a country")]
    [EditingComponentConfiguration(typeof(CountryConfigurator))]
    public string Country { get; set; }

    [EditingComponent(DropDownComponent.IDENTIFIER, Label = "State", Order = 4)]
    // Assigns 'StateConfigurator' to the field and sets 'Country' as a dependency
    [EditingComponentConfiguration(typeof(StateConfigurator), nameof(Country))]
    public string State { get; set; }
}


When the widget properties dialog is opened, the configurator ensures that the state field only shows when selecting a state for the country chosen via the Country property makes sense and populates the dropdown with relevant choices. In this example, CountryConfigurator.Configure is called on the initial load of the dialog and every time a country is selected using the Country dropdown.

Setting values of fields dynamically

Note: The ConfigureValue method described below is only available after applying hotfix 13.0.113 or newer.

You can use configurators to dynamically set the value of a field based on another field. Override the ConfigureValue(IFormFieldValueProvider, string) method of the FormComponentConfigurator base class. The ConfigureValue method is called later in the editing component’s lifecycle after the values of properties are set by the system.

The method provides the following parameters:

  • The IFormFieldValueProvider parameter allows you to access the values of properties in the given dialog that precede the component being configured (according to the Order property that can be set when assigning editing components ).
  • The string parameter contains the name of the changed field that triggered the configurator. If the field is updated for a reason other than a changed dependency field (e.g., initial rendering of the field), the value of this parameter is null.

Return the updated value of the property or, if you do not want to perform any changes, return the ConfigureValue method of the base class.

The following code block is an example that shows how to update the value of a dependent property based on updates to the value of another property:

Example - country code configurator



using System;
using System.Linq;

using CMS.Globalization;

using Kentico.Forms.Web.Mvc;

public class CountryCodeConfigurator : FormComponentConfigurator<TextInputComponent>
{
    private readonly ICountryInfoProvider countryProvider;

    // Obtains required dependencies using constructor injection
    public CountryCodeConfigurator(ICountryInfoProvider countryProvider)
    {
        this.countryProvider = countryProvider;
    }

    public override object ConfigureValue(IFormFieldValueProvider formFieldValueProvider, string changedFieldName)
    {
        // The DependencyFieldName property of the base class contains the name
        // of the property set by the EditingComponentConfiguration attribute
        if (changedFieldName == DependencyFieldName
            && formFieldValueProvider.TryGet<string>(DependencyFieldName, out var originalValue))
        {
            // Returns the code of the country selected in the Country field 
            var countryCode = countryProvider.Get(Int32.Parse(originalValue)).CountryThreeLetterCode;
            return countryCode;
        }

        // Keeps the existing property value
        return base.ConfigureValue(formFieldValueProvider, changedFieldName);
    }
}


The following code assigns the configurator to a TextInputComponent with a dependency on the value of another property:

Example - country, state and address selector usage



using Kentico.Forms.Web.Mvc;
using Kentico.PageBuilder.Web.Mvc;

public class MyWidgetProperties : IWidgetProperties
{
        [EditingComponent(DropDownComponent.IDENTIFIER, Label = "Country", Order = 3, Tooltip = "Select a country")]
        [EditingComponentConfiguration(typeof(CountryConfigurator))]
        public string Country { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Label = "State", Order = 4)]
        [EditingComponentConfiguration(typeof(StateConfigurator), nameof(Country))]
        public string State { get; set; }

        [EditingComponent(TextInputComponent.IDENTIFIER, Label = "Country code", Order = 4, Tooltip = "Enter country code")]
        // Assigns 'CountryCodeConfigurator' to the field and sets 'Country' as a dependency
        [EditingComponentConfiguration(typeof(CountryCodeConfigurator), nameof(Country))]
        public string CountryCode { get; set; }
}


When the widget properties dialog is opened, and the Country property is updated, the configurator automatically sets the value of the CountryCode property to the three-letter code of the country selected in the Country property.

You can use configurators to dynamically set the value of a field based on another field. Override the ConfigureValue(IFormFieldValueProvider) method of the FormComponentConfigurator base class. The ConfigureValue method is called later in the editing component’s lifecycle after the values of properties are set by the system.

The method provides the IFormFieldValueProvider parameter that allows you to access the values of properties in the given dialog that precede the component being configured (according to the Order property that can be set when assigning editing components ). Return the updated value of the property or, if you do not want to perform any changes, return the ConfigureValue method of the base class.

The following code block is an example that shows how to update the value of a dependent property based on updates to the value of another property:

Example - country code configurator



using System;
using System.Linq;

using CMS.Globalization;

using Kentico.Forms.Web.Mvc;

public class CountryCodeConfigurator : FormComponentConfigurator<TextInputComponent>
{
    private readonly ICountryInfoProvider countryProvider;

    // Obtains required dependencies using constructor injection
    public CountryCodeConfigurator(ICountryInfoProvider countryProvider)
    {
        this.countryProvider = countryProvider;
    }

    public override object ConfigureValue(IFormFieldValueProvider formFieldValueProvider)
    {
        // The DependencyFieldName property of the base class contains the name
        // of the property set by the EditingComponentConfiguration attribute
        if (formFieldValueProvider.TryGet<string>(DependencyFieldName, out var originalValue))
        {
            // Returns the code of the country selected in the Country field 
            var countryCode = countryProvider.Get(Int32.Parse(originalValue)).CountryThreeLetterCode;
            return countryCode;
        }

        // Keeps the existing property value
        return base.ConfigureValue(formFieldValueProvider);
    }
}


The following code assigns the configurator to a TextInputComponent with a dependency on the value of another property:

Example - country, state and address selector usage



using Kentico.Forms.Web.Mvc;
using Kentico.PageBuilder.Web.Mvc;

public class MyWidgetProperties : IWidgetProperties
{
        [EditingComponent(DropDownComponent.IDENTIFIER, Label = "Country", Order = 3, Tooltip = "Select a country")]
        [EditingComponentConfiguration(typeof(CountryConfigurator))]
        public string Country { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Label = "State", Order = 4)]
        [EditingComponentConfiguration(typeof(StateConfigurator), nameof(Country))]
        public string State { get; set; }

        [EditingComponent(TextInputComponent.IDENTIFIER, Label = "Country code", Order = 4, Tooltip = "Enter country code")]
        // Assigns 'CountryCodeConfigurator' to the field and sets 'Country' as a dependency
        [EditingComponentConfiguration(typeof(CountryCodeConfigurator), nameof(Country))]
        public string CountryCode { get; set; }
}


When the widget properties dialog is opened, and the Country property is updated, the configurator automatically sets the value of the CountryCode property to the three-letter code of the country selected in the Country property.