Example - Developing a custom form component

This page provides a step-by-step example, which demonstrates the process of creating, configuring, and registering a form component in the system.

Developing the RgbInput custom form component

In this example, we implement a form component that enables users to specify a color in the RGB hexadecimal format split into three input elements (representing the red, green, and blue components of the color, respectively) by using the HTML5 color selector. On form submit, the partial color values are concatenated using the component’s GetValue method and the resulting string inserted into the database.

You can see an example of the form component rendered as part of a form in the image below. Clicking on the Color selector opens a dialog window that allows users to select a color using a familiar interface. Once a color is selected, the color values are propagated to the three input elements via JavaScript.

Selecting a color using the color picker of the form component

Tip: To view the full code of the example, you can inspect and download the LearningKit project on GitHub. You can also run the LearningKit website by connecting the project to a Kentico database.

  1. Open your MVC project in Visual Studio.
  2. In a suitable location within your project structure (e.g., in the project’s root), create a ~/Models/FormComponentsfolder structure to store all classes holding the component’s logic.
  3. Create a new RgbInputComponentProperties properties class that inherits from FormComponentProperties<string>. In the class, implement the following:
    • Call the base class constructor from the derived class and set the data type of the underlying database column to Text, and its maximum length to 7 (the length of the hexadecimal string representing a submitted color, including the ‘#’ symbol).
    • Override the DefaultValue property and specify its editing component.



    public class RgbInputComponentProperties : FormComponentProperties<string>
    {
        // Sets the component as the editing component of its DefaultValue property
        // System properties of the specified editing component, such as the Label, Tooltip, and Order, remain set to system defaults unless explicitly set in the constructor
        [DefaultValueEditingComponent("RgbInputComponent", DefaultValue = "#ff0000")]
        public override string DefaultValue
        {
            get;
            set;
        }

        // Initializes a new instance of the RgbInputComponentProperties class and configures the underlying database field
        public RgbInputComponentProperties()
            : base(FieldDataType.Text, 7)
        {
        }
    }



  1. Create a new RgbInputComponent form component class that inherits from FormComponent<RgbInputComponentProperties, string>. Implement the following members:
    • RedComponent,GreenComponent, and BlueComponent properties used to store the partial color intensity values.
    • Override the CustomAutopostHandling property and set it to true, which disables automatic server-side evaluation of the component’s values. See Developing form components for more information.
    • Override the GetValue method. The method concatenates and normalizes the submitted partial color intensities using string interpolation.
    • Override the SetValue method which you can use to specify the initial values for each property.



    public class RgbInputComponent : FormComponent<RgbInputComponentProperties, string>
    {
        public const string IDENTIFIER = "RgbInputComponent";

        // Specifies that the property carries data for binding by the form builder
        [BindableProperty]
        // Used to store the value of the input field of the component
        public string RedComponent { get; set; } = "ff";

        [BindableProperty]
        public string GreenComponent { get; set; } = "00";

        [BindableProperty]
        public string BlueComponent { get; set; } = "00";

        // Disables automatic server-side evaluation for the component
        public override bool CustomAutopostHandling => true;

        // Gets the submitted values of the three properties and normalizes them to form a hexadecimal string of length 7,
        // e.g., in case a color was submitted in the #363 shorthand (representing #336633)
        // The returned value is subsequently saved to the corresponding column in the form's database table
        public override string GetValue()
        {
            return $"#{NormalizeReceivedValue(RedComponent)}{NormalizeReceivedValue(GreenComponent)}{NormalizeReceivedValue(BlueComponent)}";
        }

        // Normalizes individual submitted color components to 2 characters, e.g., F -> FF, 5 -> 55
        private string NormalizeReceivedValue(string value)
        {
            return value.Length == 1 ? value + value : value;
        }

        // Sets values of the properties (represented by individual 'input' elements)
        public override void SetValue(string value)
        {
            if (!String.IsNullOrEmpty(value))
            {
                RedComponent = value.Substring(1, 2);
                GreenComponent = value.Substring(3, 2);
                BlueComponent = value.Substring(5, 2);
            }
            else
            {
                SetValue("#ff0000");
            }
        }
    }



  1. Create a partial view in ~Views/Shared/FormComponents/. The view defines the visual element of the component. Note that the name of the view must correspond to the identifier you assign to the form component upon its registration to the system, i.e., _RgbInputComponent.cshtml for this example. In the view:

    • Retrieve the collection of system attributes via the ViewData.GetEditorHtmlAttributes() method (from the Kentico.Forms.Web.Mvc namespace).
    • Specify input fields for the properties defined in the main component class using the HtmlHelper.TextBoxFor extension method. Pass the collection of system attributes together with any attributes you require to the method’s htmlAttributes parameter.
    • Add an HTML5 color selector element. We recommend using a custom HtmlHelper extension method together with the TagBuilder to render custom input elements (this example uses the CustomInput extension method, the code for which is provided below). The TagBuilder automatically handles the encoding of all HTML attributes and expedites the process of writing custom inputs. The window.kentico.updatableFormHelper.updateForm(this.form) function call in the input’s onchange event ensures any depending visibility conditions are evaluated only when a new color is chosen using the color selector.
    _RgbInputComponent.cshtml
    
    
    
     @using Kentico.Forms.Web.Mvc
     @using LearningKit.FormBuilder
    
     @model LearningKit.FormBuilder.FormComponents.RgbInputComponent
    
     @* Gets a collection of system HTML attributes necessary for the correct functionality of the component input fields *@
     @{
         IDictionary<string, object> htmlAttributes = ViewData.GetEditorHtmlAttributes();
     }
    
     @{
         @* Specifies additional HTML attributes for the input fields *@
         if (htmlAttributes.ContainsKey("style"))
         {
             htmlAttributes["style"] += " width:50px;";
         }
         else
         {
             htmlAttributes["style"] = "width:50px;";
         }
    
         @* Sets the partial color inputs to read-only, ensuring users can only specify the color intensities via the color selector *@
         htmlAttributes["readonly"] = "";
     }
    
     @* Renders basic text input fields to store the partial color intensity values *@
     @Html.Raw("#")
    
     @Html.TextBoxFor(m => m.RedComponent, htmlAttributes)
    
     @Html.TextBoxFor(m => m.GreenComponent, htmlAttributes)
    
     @Html.TextBoxFor(m => m.BlueComponent, htmlAttributes)
    
     @* Specifies additional attributes for the color selector *@
     @{
         htmlAttributes.Remove("readonly");
    
         // The data attributes are used by the accompanying JavaScript logic to assign values to the input fields represented by
         // the corresponding identifiers whenever a different color is selected using the selector
         htmlAttributes["data-red-id"] = Html.IdFor(m => Model.RedComponent);
         htmlAttributes["data-green-id"] = Html.IdFor(m => Model.GreenComponent);
         htmlAttributes["data-blue-id"] = Html.IdFor(m => Model.BlueComponent);
    
         // The window.kentico.updatableFormHelper.updateForm(this.form) ensures any visibility conditions depending
         // on fields based on this component only evaluate after a color has been selected using the color selector
         htmlAttributes["onchange"] = "parseColorSelector(this); window.kentico.updatableFormHelper.updateForm(this.form)";
     }
    
     @* Renders the color selector using a custom HtmlHelper extension method *@
     @Html.CustomInput("color", "colorSelector", Model.GetValue(), htmlAttributes)
    
     <span><em>(Color selector)</em></span>
    
    
    
     

    The following excerpt contains a possible implementation of the CustomInput extension method for HtmlHelper. You can place the method to a location dedicated to HtmlHelper extension methods within your project’s structure or, for this example, create a new static FormBuilderExtensions class located in ~/FormBuilder/FormBuilderExtensions.cs. To use this extension method in the code of your views, add a using statement for the LearningKit.FormBuilder namespace.

    CustomInput HtmlHelper extension method
    
    
    
             // Renders an 'input' element of the specified type and with the collection of provided attributes
             public static MvcHtmlString CustomInput(this HtmlHelper helper, string inputType, string name, object value, IDictionary<string, object> htmlAttributes)
             {
                 TagBuilder tagBuilder = new TagBuilder("input");
    
                 // Specifies the input type, name, and value attributes
                 tagBuilder.MergeAttribute("type", inputType);
                 tagBuilder.MergeAttribute("name", name);
                 tagBuilder.MergeAttribute("value", value.ToString());
    
                 // Merges additional attributes into the element
                 tagBuilder.MergeAttributes(htmlAttributes);            
    
                 return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.StartTag)); 
             }
    
    
    
     
  2. Register the form component in the system. Place the following registration attribute over the RgbInputComponent class definition in RgbInputComponent.cs:

    
    
    
     [assembly: RegisterFormComponent(RgbInputComponent.IDENTIFIER, typeof(RgbInputComponent), "RGB color input", Description = "Allows users to specify a color in the RGB hexadecimal format either manually, or by using a color selector", IconClass = "icon-palette")]
    
    
    
     
  3. Add a file containing a JavaScript function that watches for change events fired by the color selector element and fills the partial color input fields by parsing the emitted value.

    • Create a ~/Content/FormComponents/RgbInputComponent folder in your project and place the script file there. The location ensures that the script is automatically bundled and linked in the form builder interface, and on all pages with page builder editable areas (which could contain a Form widget displaying the component).

    The code used in this sample function is intentionally written in plain JavaScript. You are free to implement the functionality in a framework of your choice.

    colorInputParser.js
    
    
    
     // Modifies the partial intensity values of the RGB input fields whenever a different color is selected
     var parseColorSelector = function (target) {
         document.getElementById(target.getAttribute('data-red-id')).value = target.value.substring(1, 3);
         document.getElementById(target.getAttribute('data-green-id')).value = target.value.substring(3, 5);
         document.getElementById(target.getAttribute('data-blue-id')).value = target.value.substring(5, 7);
     };
    
    
     
  4. (Optional) Define a new validation rule that tests whether the submitted value is in the hexadecimal format. Apply the rule to form fields based on this form component.

    To learn more about validation rules in the form builder, refer to Defining field validation rules.

    
    
    
     using System;
    
     using Kentico.Forms.Web.Mvc;
    
     using LearningKit.FormBuilder.CustomValidationRules;
    
     // Registers the validation rule in the system
     [assembly: RegisterFormValidationRule("IsHexadecimalNumberValidationRule", typeof(IsHexadecimalNumber), "Is hexadecimal number", Description = "Checks whether the submitted input is a hexadecimal string (including the leading # character).")]
    
     namespace LearningKit.FormBuilder.CustomValidationRules
     {
         [Serializable]
         public class IsHexadecimalNumber : ValidationRule<string>
         {
             // Gets the title of the validation rule as displayed in the list of applied validation rules
             public override string GetTitle()
             {
                 return "Input is a hexadecimal number.";
             }
    
             // Returns true if the field value is in the hexadecimal format
             protected override bool Validate(string value)
             {
                 // Fails if the submitted string does not contain a leading '#' character
                 if (value[0] != '#')
                 {
                     return false;
                 }
    
                 // Strips the leading '#' character
                 value = value.Substring(1);
    
                 // Tries to convert the submitted value
                 bool success = int.TryParse(value, System.Globalization.NumberStyles.AllowHexSpecifier, null, out int variable);
    
                 return success;
             }
         }
     }
    
    
     

The form component is registered in the system and ready to be used within the form builder framework. Users can insert it into forms on the Form builder tab in the Forms application.