Using Insert Options, Edit Frames And Item Renderers To Improve Experience Editor Usability For A Control With A Data Source That Has Sub Items.

High Score Labs News   •   Feb 2, 2019

Let’s imagine you want to use a control in Sitecore which has a data source as container with some subitems (for example, an accordion, slider with slider images or other lists of the same type of items). With this, you want to make it experience editor compatible, enabling content authors and marketers to easily manage it. For example, to have the ability to add, delete, sort subitems directly in the experience editor.

One of the most basic approaches in Sitecore is to use placeholders.  By placing them in the primary container rendering and then inserting every subitem rendering in to this placeholder. But, in this case we need to have an extra placeholder with a placeholder setting. In addition, in the slider rendering placeholder we would want to have only the slider image renderings. In this case, there is no clear dependency on the container and its subitems (as the subitems are located inside a container’s placeholder as a rendering with data source set). Also, instead of having a simple one rendering set in the page presentation details for a container (i.e., the slider) with one data source, we now have to use one rendering for a container (i.e., slider) and then for each container’s subitem (i.e., the slider image) we have to also use a separate rendering with its own data source. This of course can result in a huge mess of page presentation details and a big investment in rendering and placeholder management resulting in content author confusion with page structure.

There is a better approach however! To start, we would use insert options in the conjunction with Edit Frames and the Item Renderer. The result is that we would have just one rendering for a container (i.e., slider) in the page presentation details. The container data source will have subitems (i.e., slider images) as a child item. This is so we will have a clear dependency on the container and its subitems.  How is this implemented?

So, let’s say we want to have a “Slider” data source content item structured as following:

The container (Slider) template will have just one field, “Title” and subitem template (Slider Item) will have “Image”, “Link”, “Text1”, “Text2” fields.

Next, we need to setup insert options on the “Slider” template’s standard values to the “Slider Item” template. Using this setting, experience editor will allow us to add items to the container data source that are only based on certain templates:

Next, let’s create controller renderings for “Slider” and “Slider Item”:

In the “Slider” rendering, the field “Data source Template” should point to our “Slider” template. Next, set the field “Experience Editor Buttons” to the “Insert”, “Sort” and “Delete” options.

Next, the important thing to do is to set field “Renderers” (original name – “__Renderers”) of the “Slider Item” template’s standard values to the ID of the “Slider Item” rendering. This will allow us to execute the Sitecore.Mvc.Helpers.SitecoreHelper.ItemRendering() method on each “Slider Item”, which then invokes the mvc.renderRendering pipeline for the item renderings specified in the “__Renderers” field of the item:

For simplicity, lets use the popular glass mapper framework in our code.

Code mappings for our templates:

“Slider”:

[SitecoreType(TemplateId = “{B0D8C299-9C02-4DA6-912E-CA508EFFA3D2}”, AutoMap = true)]

public interface ISlider : IBaseSitecoreItem

{

[SitecoreField(“{52D74810-ABE6-44BE-8953-E68C43BC4546}”, SitecoreFieldType.SingleLineText)]

string Title { get; set; }

[SitecoreChildren(InferType = true)]

IEnumerable<ISliderItem> Items { get; }

}

“Slider Item”:

[SitecoreType(TemplateId = “{0836AA72-9DBE-4D96-89F1-27D19ECF09AB}”, AutoMap = true)]

public interface ISliderItem : IBaseSitecoreItem

{

[SitecoreField(“{4A6B00DB-6EA3-41B4-ACAC-018556EC6909}”, SitecoreFieldType.Image)]

Image Image { get; set; }

[SitecoreField(“{CE0320CE-4D0E-4788-B16D-DBF9956CBA0E}”, SitecoreFieldType.SingleLineText)]

string Text1 { get; set; }

[SitecoreField(“{5399D2DE-B4B7-4EA8-8BB9-89357A9C395B}”, SitecoreFieldType.SingleLineText)]

string Text2 { get; set; }

[SitecoreField(“{6AF2167E-96FF-41CC-AB56-6865D28E1723}”, SitecoreFieldType.GeneralLink)]

Link Link { get; set; }

}

base model:

public interface IBaseSitecoreItem

{

[SitecoreId]

ID ID { get; set; }

[SitecoreItem]

Item InnerItem { get; set; }

[SitecoreInfo(SitecoreInfoType.Name)]

string ItemName { get; set; }

[SitecoreInfo(SitecoreInfoType.DisplayName)]

string ItemDisplayName { get; set; }

[SitecoreInfo(SitecoreInfoType.Language)]

Language Language { get; set; }

}

And our controller with related actions for our renderings:

public class ComponentsController : GlassController

{

public ActionResult Slider()

{

var model = this.GetDataSourceItem<ISlider>();

return View(model);

}

public ActionResult SliderItem()

{

var model = this.SitecoreContext.Cast<ISliderItem>(RenderingContext.Current.Rendering.Item);

return View(model);

}

}

Slider.cshtml:

@using Sitecore.Feature.Components.Models

@using Sitecore.Globalization

@using Sitecore.Mvc

@using Glass.Mapper.Sc.Web.Mvc

@inherits GlassView<ISlider>

@Editable(x => x.Title)

@foreach (var item in Model.Items)

{

@Html.Sitecore().ItemRendering(item.InnerItem)

}

SliderItem.cshtml:

@using Sitecore.Feature.Components.Models.GlassMapper.BoxedAd

@using Sitecore.Feature.Components.Models

@using Sitecore.Globalization

@using Sitecore.Mvc

@using Glass.Mapper.Sc.Web.Mvc

@inherits GlassView<ISliderItem>

@using (BeginEditFrame(“/sitecore/content/Applications/WebEdit/Custom Experience Buttons”, Model.ID.ToString(), Model.ItemName))

{

using (BeginRenderLink(m => m.Link, isEditable: true))

{

@RenderImage(x => x.Image, isEditable: true)

<h3>@Editable(x => x.Text1)</h3>

<p>@Editable(x => x.Text2)</p>

}

}

So, in the Slider.cshtml we will render html markup for the “Slider” rendering with the “Slider” data source content item and for each of this datastore child item, with the “Slider Item” template. The code snippet is:

@foreach (var item in Model.Items)

{

@Html.Sitecore().ItemRendering(item.InnerItem)

}

The Html.Sitecore().ItemRendering() extension method from Sitecore.Mvc.Helpers namespace executes rendering pipeline for an item (renderings specified in the __Renderers field) and returns HtmlString for this  item.

As, a small note – this is possible to use a “view rendering” template instead of the “controller rendering” for the “Slider Item” rendering, in this case controller action “SliderItem” do not required, but the SliderItem.cshtml markup can stay the same.

Next, create a page item with our “Slider” rendering, set the data source for it to our “Slider” content item.

As a result, in the experience editor, our “Slider” rendering now has the edit frame buttons “Insert”, “Sort” and “Delete”. The “Insert” button will open a popup dialog, allowing you to insert a new item with a “Slider Item” template as a child for our “Slider” data source item. The “Sort” button will now  open a popup dialog, allowing us to sort its subitems. Finally, the “Delete” button will delete “Slider” rendering from the page presentation details.

On each “Slider Item” edit frame we will have our buttons to delete, move up, move down, for our  subitem:

In this walkthrough, we used the default location to create our edit frame and buttons, located in the core database at /sitecore/content/Applications/WebEdit/Custom Experience Buttons. Generally with Sitecore it is a good idea to make a copy and make your desired changes to the duplicate, just in case Sitecore decides to upgrade or move those items in the future. Simply make the needed path changes to do this as shown above.

Despite  the advantages using this technique; maintainable data source structure, no excessive placeholders, more manageable renderings for subitems in the page presentation details. There are also a few things to consider however. The first major one is that there is no way (Out of the box) to use the Sitecore personalization feature on the container subitems (just because there is no rendering for it in the page presentation details). As an option though, you can personalize the container rendering (“Slider” rendering in our case) with the new “Slider” data source  with it new subitems.