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.

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/FormComponents folder structure to store all classes holding the component's logic. 3. Create a new RgbInputComponentProperties properties class that inherits from FormComponentProperties. 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.

https://docs.xperience.io 1 3.

Example - Developing a custom form component

public class RgbInputComponentProperties : FormComponentProperties { // 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) { } }

4. Create a new RgbInputComponent form component class that inherits from FormComponent. 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.

https://docs.xperience.io 2 4.

Example - Developing a custom form component

public class RgbInputComponent : FormComponent { // 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"); } } }

https://docs.xperience.io 3 4.

Example - Developing a custom form component

5. 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 Kent ico.Forms.Web.Mvc namespace). Specify input fields for the properties defined in the main component class using the HtmlHelper.TextBoxFor ex tension 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 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 *@ @{

https://docs.xperience.io 4 5.

Example - Developing a custom form component

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)

(Color selector)

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 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)); }

6. Register the form component in the system. Place the following registration attribute over the RgbInputComponent class definition in RgbInputComponent.cs:

[: RegisterFormComponent("RgbInputComponent", 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")]

https://docs.xperience.io 5 6.

Example - Developing a custom form component

7. 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); };

8. (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.

https://docs.xperience.io 6 8.

Example - Developing a custom form component

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 { // 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.

https://docs.xperience.io 7