Blog

Tagged by 'widgets'

  • There are times when you need to know what widgets are being used on a page. In my case, I needed to know this information to render JavaScript code at the bottom of the page that each of my widgets depends on.

    Why don't I just place all the JavaScript code my site and widgets use in one file? Loading one large JavaScript file isn't the best approach for page performance. Instead, I use LabJS to dynamically load scripts in specific execution order without blocking other resources. So if I created a Carousel widget in Kentico, I would only load the JavaScript plugin if added to the page.

    I'll use my JavaScript scenario as a basis for demonstrating the way to list out widgets used in a page.

    If we delve into the CMS_Document table, Kentico uses the "DocumentPageBuilderWidgets" field that stores a JSON structure consisting of a list of all the widgets and their property values. All we are interested in is the type property.

    Let's get to the code.

    Controller - SharedController

    I created a SharedController class containing a GenerateWidgetJavascript() PartialViewResult. This will convert the JSON from the "DocumentPageBuilderWidgets" field into a JSON Object to then be queried (using SelectTokens) to select every iteration of the type field in the hierarchy.

    /// <summary>
    /// Get widget used on a page to render any required JavaScript.
    /// </summary>
    /// <returns></returns>
    public PartialViewResult GenerateWidgetJavascript()
    {
        List<string> widgetTypes = new List<string>();
    
        if (Page.GetStringValue("DocumentPageBuilderWidgets", string.Empty) != string.Empty)
        {
            JObject pageWidgetJson = JObject.Parse(Page.GetStringValue("DocumentPageBuilderWidgets", string.Empty));
    
            if (pageWidgetJson != null)
                widgetTypes = pageWidgetJson.SelectTokens("$.editableAreas[*].sections[*].zones[*].widgets[*].type").Select(jt => jt.ToString().Substring(jt.ToString().LastIndexOf('.') + 1)).Distinct().ToList();
        }
    
        return PartialView("Widgets/_PageWidgetJs", widgetTypes);
    }
    

    Additional manipulation is carried out on the type field using LINQ to return a distinct set of results, as there might be a case where the same widget is used multiple times on a page. Since I name all my widgets in the following format - <SiteName>.<WidgetGroup>.<WidgetName>, I am only interested in the <WidgetName>. For example, my widget would be called "SurinderSite.Layout.Carousel". The controller will simply output "Carousel".

    To avoid confusion in my code snippet, it's worth noting I use a Page variable. This contains information about the current page and is populated in my base controller. It has a type of TreeNode. You can see my approach to getting the current page information in this post.

    Partial View - _PageWidgets

    The most suitable place to add my widget dependent JavaScript is in the /View/Shared/Widgets directory - part of the recommended Kentico project structure.

    All we need to do in the view is iterate through the string collection of widget types and have a bunch of if-conditions to render the necessary JavaScript.

    @model List<string>
    
    @if (Model.Any())
    {
        <script>
            @foreach (string widgetType in Model)
            {
                if (widgetType == "Carousel")
                {
                    <text>
                        $LAB
                            .script("/resources/js/plugins/slick.min.js")
                            .script("/resources/js/carousel.min.js").wait(function () {
                                {
                                    FECarousel.Init();
                                }
                            });
                    </text>
                }
            }
        </script>
    }
    

    Layout View

    The Layout view will be the best place to add the reference to our controller in the following way:

    @{ Html.RenderAction("GenerateWidgetJavascript", "Shared"); }