Sitecore Item Renderers

One of the challenges with Sitecore is that it is so flexible that you can break it, bend it and modify it beyond recognition. This can have deep effects in how users interact with the system and how they perceive the quality of the product. Sitecore can be a great product when used wisely or it can also rapidly become a bag of nails when it falls in the hands of the wrong coders.

A few months ago, whilst doing Sitecore training, one of the students working for a company that had used Sitecore for a while, told me about the ire of the authors using the system. Due to the way the system had been set up something as simple as creating a new page on the website, which should take minutes at the most, was taking them potentially an hour! Obviously those authors thought Sitecore was a terrible product; the reality of course is that it had simply not been set up correctly.

They were taking an alarmingly long time to create a new page, not because the system was running slow, but because the developer(s) had created a beautifully generic solution for modelling the content of pages, which required the author to create lots of items to create a single page. This is such a common pitfall when developing: generalise when is not needed at the expense of complexity or usability. Another important and obvious consideration when creating a Sitecore site is that you are building a "living" website; authors will have to add and modify content on the website frequently.

What was most surprising is that with a surprisingly small amount of changes the author's situation could improve dramatically. They should be able to create new pages in minutes whilst keeping the same basic structure of their solution.

The challenge

What drove the developers to use multiple items to model a page? The road towards flexibility. They wanted to provide authors with the freedom to create whatever complex page they wanted. So they would provide them with a set of building blocks which could be combined at will to form the page. Can't you do that using Sitecore's rendering engine? Yes, you could, but most likely you still need to keep the datasources somewhere, so adding them underneath the page is not a bad solution.

Multiple items modelling a single page

You can imagine the downside of this set up. From the author's perspective, in order to create a page they have to:

  1. Create the actual page item
  2. Add a folder item underneath
  3. Create and add data to all the other items that store the content of the page
  4. Modify the presentation details for the page item, adding a component (and the right component!) for each of the items inside the folder, setting its datasource and correct placeholder.

Presentation details for widgets

This is not a trivial process even for an avid Sitecore user using a nice and responsive Sitecore application.

The solution

How could this be improved? If there were any common combination of items forming pages they could be defined as branches. This would allow them to create the item, the folder, and potentially some other items in a single operation. This literally takes a few seconds to define and already helps authors.

However the biggest issue is forcing them to fiddle with the presentation details. The challenge is that the components needed on the page will vary according to the datasources they have added to the widgets folder below the item. This is not a problem when just creating new items, but also whenever they change the structure of an existing page.

Ideally, the presentation details of the actual page should have a single component that retrieves and renders all the items inside the widgets folder. This way, the authors can modify the widgets, by adding, editing, re-ordering or removing items; but they never have to fiddle with the presentation details.

Single renderer on the presentation details

Sitecore has the right tool for this, it is the Item Renderer. This exists in the form of a component /sitecore/layout/Renderings/System/ItemRenderer and an extension method @Html.Sitecore().ItemRendering(...). You will bind a datasource to it (or pass an item as a paremeter when using the method). It will then render said item. How will it render it? Using whatever rendering(s) you have bound to its __Renderers Standard Field.

This is exactly what is needed here, since you want to use a common component on the presentation details, and delegate the actual rendering to whatever component is defined in each individual (widget) item.

So for example, if we have a view rendering that renders the headline widget, we will add its ID to the __Renderers field in the Standard Values of the Headline template. When we then use the ItemRenderer and set as datasource an item based on the Headline template, it will use the correct component.

The Single component that renders all the widgets, just needs to iterate over all the items inside the widgets folder and invoke the ItemRenderer method on each of them.

var widgets = contextItem?.Children["Widgets"]?.Children;

return widgets == null ? (ActionResult) new EmptyResult() : View(widgets);  

and in the view, simply:

@foreach (var widget in Model)
{
    @Html.Sitecore().ItemRendering(widget)
}

You can download a working example from Github.

The __Renderers field accepts also multiple pipe-separated IDs of a component definition item. I believe it also accepts a custom XML format (akin to presentation details) which does not seem to be documented anywhere.

A more generic solution would be to implement datasource queries: this would allow you to define a query (maybe as a Sitecore query syntax) to bind multiple items as the datasource of a component (instead of single one). Then you could create an expression that would get the correct widgets and pass them to an ItemRenderer rendering. That's blog post for another day.