Developing modal dialogs for builder component properties

Modal dialogs are an alternative way of editing the properties of form or page builder components in the administration interface (not on the live site). When configuring a builder component, modal dialogs allow you to display an additional pop-up window, where the content editor can adjust the component’s properties.

The system supports modal dialogs in the following locations:

Modal dialogs are suitable for advanced property editors with potentially large dimensions. Such editors would otherwise seize a great part of the configuration dialog or cover the content of widgets if implemented as inline editors. Typical examples of modal dialogs are selectors or complex editors with multiple required inputs.

The system provides a JavaScript API that helps developers open modal dialogs and integrate them into the administration interface. Developers have unlimited freedom with regards to the implementation of the modal dialog window itself.

On this page, you can find instructions and best practices for the development of modal dialogs. To implement a modal dialog, you need to:

  1. Develop MVC elements that define the markup of the dialog window. See Creating dialog markup.
  2. Prepare inline editors or form components for the properties where you want to use the dialog.
  3. Add scripts that open the modal dialog to the given inline editors or form components. See Opening dialogs.

You can find a Basic example of a modal dialog implementation below.

Using page builder data selectors

By default, the system contains form components and JavaScript API support to select data from the content tree (pages) and media libraries (media files), which can be used in the page builder interface.

Creating dialog markup

Create Model/View/Controller elements that represent the modal dialog according to the MVC best practices.

  • Model
    • Store the model in the ~/Models/ModalDialogs folder.
  • View
    • Store the modal dialog view in the ~/Views/Shared/ModalDialogs/<DialogName>folder.
    • The view must define a full HTML page, including html, head and body tags. This is required because the model dialog is loaded as a separate HTML document in an iframe.
    • The view must contain a link to modal dialog scripts. Use the @Html.Kentico().ModalDialogScript() extension method (available in the Kentico.Components.Web.Mvc.Dialogs namespace) in the head section of the markup.
    • The view must contain links to all necessary resources, such as related stylesheets and scripts.
    • Within scripts in the view, you can use the following functions available in the kentico.modalDialog namespace:
      • open(modalDialogOptions) – opens a new modal dialog. See below.
      • close() – closes the modal dialog without applying the changes.
      • apply() – applies the changes and closes the modal dialog.
      • getData() – returns the data object provided when the dialog was opened using the kentico.modalDialog.open function. See the data property of the modal dialog options object below.
      • contentSelector.open(contentSelectorOptions) – enables you to open the content selector dialog. For more information, visit the content selector JavaScript API documentation.
  • Controller
    • Store the modal dialog controller in the ~/Controllers/ModalDialogs folder.

Opening dialogs

To open a modal dialog, use the kentico.modalDialog.open function in an inline editor script or as part of a form component script. Provide an options object with the following properties as a parameter of the function:

Required properties

Property

Description

url

The URL of the model dialog content. Typically, use the URL matching the route of the controller action that displays the modal dialog’s view.

The route URL must be generated and resolved on the server and you need to ensure that it is propagated to the client script. For example, you can use one of the following approaches:

  • Include the related script in the view of the editing component (and generate the modal dialog URL directly).
  • Serialize the generated URL and any other required data into a JSON string within the editing component view, and pass the result as the parameter of a client function.
  • Set the generated URL into an attribute of an element in the editing component’s markup, and retrieve the value in the script using either data attributes or the getAttribute method.
Editing component markup



<button type="button" onclick="openSampleModalDialog()">Open dialog</button>

<script type="text/javascript">
    function openSampleModalDialog() {
        kentico.modalDialog.open({
            url: @Url.Action("Index", "SampleModalDialog"),
            ...
        });
    }
</script>


Note: If you need to access any content requiring authentication (e.g. the current user), you need to additionally use the AuthenticateUrl extension method (available in the Kentico.Content.Web.Mvc namespace) to add an appropriate authentication hash to the URL.

applyCallback

A callback function invoked when the dialog’s confirmation button is clicked. Within this function, you need to ensure retrieving of the value selected in the dialog and propagation of the value to the component which opened the dialog.

To propagate the value from the modal dialog to the component which invoked the modal dialog:

  • If the dialog was opened from a widget through an inline editor, use the updateProperty custom event
  • If the dialog was opened from a form component, modify the value of the input element.

Optional properties

Property

Description

applyButtonText

A string property allowing you to set a custom label for the dialog confirmation button.

cancelButtonText

A string property allowing you to set a custom label for the dialog cancel button.

cancelCallback

A callback function invoked when the dialog is closed. Similar to applyCallback(see above).

data

An object which can be used to pass data of any type to the dialog (like a model in MVC architecture). This object can be retrieved in a script within a dialog using the kentico.modalDialog.getData function.

maximized

A boolean value which indicates whether the dialog is displayed in maximized size. If set to true, it overrides the width property.

showFooter

A boolean value which indicates whether the footer of the dialog is shown.

theme

A string value that sets the header color for the dialog, so that it matches the interface element from which the dialog was opened. Choose one of the following available themes:

  • widget (blue)
  • section (gray)
  • template (orange)

The default dialog theme is widget (blue). If a modal dialog is opened from a configuration dialog (via a form component), the theme inherits from the given configuration dialog.

title

A string property whose value is displayed in the header when the dialog is opened in the user interface.

width

CSS width property of the modal dialog. For example “250px” or “80%”.

This property can be affected by the maximized property.

Example – Developing a simple modal dialog

The following example leads you through the process of developing a modal dialog that allows users to select the background color of a widget. This dialog can be opened either through an inline editor, or through a button in the configuration dialog of a widget.

Note: The following example is based on the LearningKit project. To use the code samples in your project, you need to modify the namespaces, identifiers and other occurrences where LearningKit is mentioned to match your project’s name.

Prerequisites

You need to implement the following components to be able to utilize the modal dialog:

  • A widget with a property that determines its background color.
  • An inline editor containing a button that opens the modal dialog.
    – OR –
    A form component containing a text input field and a button that opens the modal dialog.

Create a ColorModalDialogViewModel.cs file in the ~/Models/ModalDialogs folder:

ColorModalDialogViewModel.cs



using System.Collections.Generic;

namespace LearningKit.Models.ModalDialogs
{
    public class ColorModalDialogViewModel
    {
        public IEnumerable<string> Colors { get; set; }
    }
}


Create a _ColorModalDialog.cshtml file in the ~/Views/Shared/ModalDialogs/ColorModalDialog folder:

_ColorModalDialog.cshtml



@using Kentico.Components.Web.Mvc.Dialogs

@model LearningKit.Models.ModalDialogs.ColorModalDialogViewModel

<!DOCTYPE html>
<html>
<head>
    @Html.Kentico().ModalDialogScript()
    <meta name="viewport" content="width=device-width" />
</head>
<body>
    <form action="">
        @foreach (var color in Model.Colors)
        {
            <input type="radio" id="@color" name="color" value="@color" />
            <label for="@color">@color</label><br />
        }
    </form>

    @* Script that preselects the radio button of the current color
       Gets the value from the data passed when the 'kentico.modalDialog.open' function is called *@
    <script type="text/javascript">
        document.addEventListener("DOMContentLoaded", function () {
            var defaultValue = kentico.modalDialog.getData().value;
            document.getElementById(defaultValue).checked = true;
        });
    </script>
</body>
</html>


Create a ColorModalDialogController.cs file in the ~/Controllers/ModalDialogs folder:

ColorModalDialogController.cs



using System.Collections.Generic;
using System.Web.Mvc;

using LearningKit.Models.ModalDialogs;

namespace LearningKit.Controllers.ModalDialogs
{
    public class ColorModalDialogController : Controller
    {
        public ActionResult Index()
        {
            var model = new ColorModalDialogViewModel
            {
                Colors = new List<string>() { "red", "blue", "white", "green", "black", "gray", "yellow" }
            };

            return View("ModalDialogs/ColorModalDialog/_ColorModalDialog", model);
        }
    }
}


Opening the modal dialog

Use one of the following approaches.

Form component

In the view of the form component, generate the modal dialog URL and pass it to the client function that opens the model dialog:




@using Kentico.Forms.Web.Mvc

@model LearningKit.FormBuilder.FormComponents.ColorFormComponent

@{
    // Gets a collection of system HTML attributes necessary for the functionality of form component inputs
    IDictionary<string, object> htmlAttributes = ViewData.Kentico().GetEditorHtmlAttributes();

    // Prepares data that will be passed to the function that opens the modal dialog
    string dialogData = Newtonsoft.Json.JsonConvert.SerializeObject(new
    {
        modalDialogUrl = Url.Action("Index", "ColorModalDialog"),
        colorInputId = Html.IdFor(m => m.Value).ToHtmlString()
    });
}

@* Textbox input element that displays/edits the form component's color value *@
@Html.TextBoxFor(m => m.Value, htmlAttributes)

@* Button that opens the modal dialog when clicked *@
<button type="button" class="ktc-btn ktc-btn-default" onclick="openColorModalDialog(@dialogData)">Open dialog</button>


Execute the following function when a button to open the modal dialog is clicked:




function openColorModalDialog(dialogData) {
    // Gets the form component's input element
    var inputElement = window.document.querySelector('#' + dialogData.colorInputId);

    // Opens the modal dialog
    kentico.modalDialog.open({
        url: dialogData.modalDialogUrl,
        applyCallback: function(dialogWindow) {
            // Retrieves the selected value from the modal dialog
            var selectedValue = dialogWindow.document.querySelector('input[name="color"]:checked').value;
            // Updates the value of the input element in the property configuration dialog
            inputElement.value = selectedValue;
        },
        applyButtonText: 'Confirm color selection',
        title: 'Select a color',
        // Passes the current color to the modal dialog data
        data: { value: inputElement.value }
    });
}


Inline editor

In the view of the inline editor, generate the modal dialog URL and pass it as an HTML attribute argument of the BeginInlineEditor function (which generates the inline editor wrapping element).




@using Kentico.PageBuilder.Web.Mvc
@using Kentico.Web.Mvc

@model LearningKit.Models.InlineEditors.ColorEditor.ColorEditorModel

@using (Html.Kentico().BeginInlineEditor("color-editor",
                                         Model.PropertyName,
                                         new { data_url = Url.Action("Index", "ColorModalDialog") }))
{
    <div style="position: absolute; top: 0px; right: 20px;">
        <button id="modal-btn" type="button">Change color</button>
    </div>
}


Modify the init property of the registration function in the inline editor’s script:




        init: function (options) {
            var editor = options.editor;

            // Adds a click action for the 'Modal dialog' button
            editor.querySelector("#modal-btn").addEventListener("click", function () {
                // Opens the modal dialog window
                kentico.modalDialog.open({
                    // Gets the modal dialog URL from the 'data-url' attribute of the inline editor wrapping element
                    url: editor.getAttribute("data-url"),
                    applyCallback: function (dialogWindow) {
                        // Creates and dispatches an event that notifies the widget about changes of properties
                        var event = new CustomEvent("updateProperty", {
                            detail: {
                                // Retrieves the color value from radio buttons within the modal dialog window
                                value: dialogWindow.document.querySelector('input[name="color"]:checked').value,
                                name: options.propertyName
                            }
                        });
                        editor.dispatchEvent(event);
                    },
                    applyButtonText: "Confirm color selection",
                    title: "Select a color",
                    // Passes the current color value to the modal dialog data
                    data: { value: options.propertyValue }
                });
            });
        }