Sitecore Forms and GraphQL in harmony – building custom control (Part 1)

Happy people, happy new year! A new and a wonderful year is waiting for you. Specially now when Sitecore released the best release ever – Sitecore 9.1.

But before we start, I need to repeat myself about Blazor. Like the Roman senator Cato(the Elder) concluded to all his speeches in the senate –

Carthago delenda est(Carthage must be destroyed)

I must emphasize the importance of Blazor – the game changer of the decade! It will allow you to build a single-page web app in c# that runs in the browser with WebAssembly – no need for javascript anymore. In the ASP.NET Core 3.0 release, Microsoft will have a (pre)server-side version of Blazor(called Razor components) using client-side with SignalR.
I’ve setup a client-side Blazor Sitecore project at github – SitecoreBlazor. Please check out my post Time travel into the future – BLAZOR + SITECORE + HELIX.

Blazor futurum sit(Blazor is the future)

Let’s move on to today’s post. It’s about the wonderful world of GraphQL and combining it with Sitecore, in this case Sitecore Forms.

Sorry guys, I had to make two posts of this. So the first part will cover how to build a custom control for Sitecore Forms. The second part will be about the lovely GraphQL 🙂

To show you how helpful GraphQL can be in your web app’s, I will make a custom Sitecore Form‘s control – Tree view list. The control will be similar to the drop tree control in Sitecore, where you will be able to select a root/start node but also what “valid” template the descendants should have. We will do this by using GraphQL.

First of all we need the JavaScript component. It will be our tree view list and there are ton’s of good stuff out there. How about this one:
Multi-Select Drop Down Tree Plugin With jQuery – Combo Tree
And yes it feels a bit old-fashioned to use jQuery but let’s use this one.

It’s quite easy to use. We need an input field, some json data(myData) and that’s it:

<input type="text" id="example" placeholder="Select">

$('#example').comboTree({
  source : myData
});

Ok, next thing is to make the custom control in Sitecore Forms.
First we will create a template for the Tree View list. We should only use/inherit the basic stuff to our template:
Field – /sitecore/templates/System/Forms/Fields/Field
Label Settings – /sitecore/templates/System/Forms/Label Settings
Validation Settings – /sitecore/templates/System/Forms/Validation Settings
Tracking Settings – /sitecore/templates/System/Forms/Tracking Settings
Save Settings – /sitecore/templates/System/Forms/Save Settings
Value Provider Settings – /sitecore/templates/System/Forms/Value Provider Settings

We will put the new template in /sitecore/templates/Foundation/Forms/Fields/:

The new fields we created are:
Field RootNode will contain the item id of the start item.
Field ChildrenTemplate will contain the “valid “template id for the descendants/children.
Field JsonDatasource will contain json data if we don’t wan’t to use data from Sitecore.

Next will be to create the field type for the tree view list, let’s put it in the Lists folder:

Here we need to set where the razor view is located and also what viewmodel to use.
We also need to set the Field template, we will select the “tree view list” template we created earlier. By default it’s a drop tree control that points to /sitecore/templates/System/Forms/Fields. You should not create your templates under System(advice from Mr Laub), therefor we have our template in /sitecore/templates/Foundation/Forms/Fields/. But in order to set it we need to turn on the “Raw values” and paste the id from our newly created “tree view list” template.

We need to go back to our newly created “tree view list” template and set the above field type in Standard Values:

We are not quite ready with the field type we’ve created. We need to set the Property Editor’s field(it’s a drop tree field that points to property settings located in core database).

A property setting shows the “properties” for a field in the Sitecore Forms editor.

That means we need to create a new property setting, the best way to do this is to use Sitecore Rocks(my personal opinion).
So let’s fire up Sitecore Rocks(which works fine in Sitecore 9.1) and go to a similar property setting, SingleLineText, and copy it. We will call it TreeList.

Now we need to set/create the property fields.
First off is the JsonDatasource field, that’s the one we use when we want to show “no Sitecore data” in the tree view list component. Right click on the Details to select Add New Item and locate the FormTextBox Parameters template:

We will set some label texts and most importantly, we need to map the value from this text box to the JsonDatasource property in the viewmodel(the one we set in the tree view list template). Notice the BindingConfiguration field, here we will do the mapping. *We will come back to the creation of the viewmodel.

Next is the RootNode field, this is for selecting a start item/root node in Sitecore. Right click on the Details to select Add New Item and locate the FormItemTreeView Parameters template and name it RootNode. In the Data section we will set the Database field to $context_contentdatabase.

And we also need to map the SelectedItemId field to the RootNode property(from the viewmodel).

The last field is the ChildrenTemplate field, it’s when listing children/descendants we only want to show items using a specific template. Right click on the Details to select Add New Item and locate the FormItemTreeView Parameters template and name it ChildrenTemplate. In the Data section we will set the Database field to master and in the StaticData field we will set the path /sitecore/templates.

We also need to map the SelectedItemId field to the ChildrenTemplate property(from the viewmodel).

Finally, we need to update the the Details item and add/select our newly created items:

Here is the outcome of our newly created TreeList property setting. Notice the new fields:
Text box – Json data
Tree view – Select start node
Tree view – Select template for the children

Ok good people are you still with me 🙂

Final part of the control is the razor view and the viewmodel.
The viewmodel – TreeViewDropDownListViewModel. This is the one that will hold the data for the razor view. Notice the properties JsonDatasource, RootNode and ChildrenTemplate. That’s the ones we binded/mapped to earlier when we did the TreeList property setting.

namespace Foundation.SitecoreForms.Models
{

    using Sitecore.Data.Fields;
    using Sitecore.Data.Items;
    using Sitecore.Diagnostics;
    using Sitecore.ExperienceForms.Mvc.Models.Fields;
    using System;

    [Serializable]
    public class TreeViewDropDownListViewModel : InputViewModel<string>
    {

     
        public string JsonDatasource { get; set; } = string.Empty;

        public string RootNode { get; set; } = string.Empty;

        public string ChildrenTemplate { get; set; } = string.Empty;
       

        protected override void InitItemProperties(Item item)
        {
            Assert.ArgumentNotNull(item, nameof(item));
            base.InitItemProperties(item);

            JsonDatasource = item.Fields[Templates.TreeViewDropDownList.Fields.JsonDatasource]?.Value;
            RootNode = item.Fields[Templates.TreeViewDropDownList.Fields.RootNode]?.Value;
            ChildrenTemplate = item.Fields[Templates.TreeViewDropDownList.Fields.ChildrenTemplate]?.Value;

        }

        protected override void UpdateItemFields(Item item)
        {
            Assert.ArgumentNotNull(item, nameof(item));
            base.UpdateItemFields(item);

            Field jsonField = item.Fields[Templates.TreeViewDropDownList.Fields.JsonDatasource];
            jsonField?.SetValue(JsonDatasource, true);

            Field rootNodeField = item.Fields[Templates.TreeViewDropDownList.Fields.RootNode];
            rootNodeField?.SetValue(RootNode, true);

            Field childrenTemplatesField = item.Fields[Templates.TreeViewDropDownList.Fields.ChildrenTemplate];
            childrenTemplatesField?.SetValue(ChildrenTemplate, true);

        }


    }
}

Since we only want to use the basic stuff, it’s enough to inherit class Sitecore.ExperienceForms.Mvc.Models.Fields.InputViewModel<string>

Here is the razor view. Notice the attributes on the input field: data-rootnode and data-childrentemplate. They will be used later when we will do some GraphQL magic 😉

@using System.Web.Mvc
@using System.Web.Mvc.Html
@using Foundation.SitecoreForms
@using Sitecore.ExperienceForms.Mvc.Html
@using Foundation.SitecoreForms.Models
@using Sitecore.Mvc

@model TreeViewDropDownListViewModel


@helper GenerateScript()
{
    if (Html.Sitecore().IsExperienceFormsEditMode() || !string.IsNullOrWhiteSpace(Model.RootNode))
    {
        return;
    }

    <script type="text/javascript">

        //Fallback json input
        var jsonData = @Html.Raw(Model.JsonDatasource);


        jQuery(document).ready(function() {

            var tree = jQuery("#@Html.IdFor(m => Model.Value)");

            tree.comboTree({
                source: jsonData
            });

        });

    </script>

}



<label for="@Html.IdFor(m => Model.Value)" class="@Model.LabelCssClass">@Html.DisplayTextFor(t => Model.Title)</label>
<input id="@Html.IdFor(m => Model.Value)" name="@Html.NameFor(m => Model.Value)" class="@Model.CssClass" type="text" value="@Model.Value"  data-sc-tracking="@Model.IsTrackingEnabled"  data-rootnode="@Model.RootNode" data-childrentemplate="@Model.ChildrenTemplate" data-sc-field-name="@Model.Name" data-sc-field-key="@Model.ConditionSettings.FieldKey" @Html.GenerateUnobtrusiveValidationAttributes(m => m.Value) />
@Html.ValidationMessageFor(m => Model.Value)

@GenerateScript()


If not the start/root node is set we assume that we will use Model.JsonDatasource and do some ugly inline scripting 😉

The Html.Sitecore().IsExperienceFormsEditMode() is a helper class:

namespace Foundation.SitecoreForms
{
    using Sitecore.Mvc.Helpers;

    public static class HtmlHelper
    {

        private const string SitecoreFormMode = "sc_formmode";

        public static bool IsExperienceFormsEditMode(this SitecoreHelper helper)
        {
            return Sitecore.Context.Request.QueryString[SitecoreFormMode] != null;
        }
    }
}

The post never ends, I think I will stop here otherwise no one will read it.
Stay tuned for part 2. Give me a day and you will have it.

That’s all for now folks 🙂


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.