Sitecore True Item Rendering

Vasiliy Fomichev

In Best Practices, Development Posted

With the release of the MVC framework support in Sitecore several new renderings have been added to Sitecore developer’s arsenal: Method, URL, Controller, View, and Item renderings. I was excited to see an Item Rendering finally being offered as part of the default set, as it allows to follow the Separation of Concerns principle a bit further, by letting non-page type content items carry their own presentation layer. Almost immediately after learning how the new Item Renderings worked, I felt very disappointed… Instead of using the referenced item’s presentation layer, it pulled the rendering list by reading GUIDs from a Renderers field that is hidden by default. That was definitely not what I was expecting, so I set off to make a new version of the item rendering, something similar to what I have been before.

Why the awkward Sitecore Item Rendering?

Yes, why? Why would Sitecore want to torture their content editors by not allowing them to use the already existing presentation layer defined on the item to display it, but rather make them look for some abstract GUID for a rendering, copy and paste it into a free-entry text field called Renderers (I wonder, if the architect who designed this at Sitecore ever tried explaining how the Item Rendering worked to a non-technical person or what a GUID was?) To top things off, if that was not enough of a torture, the Renderers field is not shown unless the Standard Fields checkbox is checked in the View tab of the Content Editor.

All scenarios that came to mind that could potentially justify having such implementation had limited functionality and were not flexible or sound enough to say “ok, I get it!”. Many use cases that came close to being solid, could be much better implemented using other types of renderings.

Having come up with no other excuses for this functionality other than a “trial-type limited functionality piece for additional flexibility and rare edge cases”, I went on to creating a True Item Renderer.

 

Why do we need a True Item Rendering?

 Separation of Concerns

Sitecore has been doing a great job separating the base data layer from presentation, which I refer to as horizontal separation, however, things have been left up to developers to make sure that the Separation of Concerns was followed in hosted implementations, but this is where things got a bit muddy.

Sitecore Renderers field.

Sitecore Renderers field.

Let’s take a look at a hypothetical scenario – a client asks to add a menu to the right-hand column of the website; nothing fancy, just a simple unordered list of links. After receiving the requirements, our developer smiles, gives a ridiculously overestimated level of effort, and goes on to implementing it in the quickest way possible. Most likely she would create a new template (if one does not yet exist) and call it Link, which would host a single Generic-Link –type field called Link. There would be a folder somewhere in the Sitecore content tree that could host several items created from the new Link template, and a “Links List” rendering would be created that would have its Datasource pointed to that folder and the logic would iterate through the links within it, extract all required info, and render the required unordered list of HTML hyperlinks – easy! The problem with this setup is that now the “Links List” rendering dictates the presentation for the Link-type items, which breaks the vertical Separation of Concerns, where each item should know how to display itself. At this point you might call me a “purist” and roll your eyes, but hold that thought.

Things get a bit more complicated, if the client comes back and says, “Hey, the list looks great! However, yesterday I spoke with the CRO guru, who advised us to put an image of a donkey right between the links number two and three, which should improve our conversion rate dramatically. Could you add that real quick for me?” As a good and optimistic developer by nature you would say –“Yes, sure thing!”, and quietly walk back to your desk thinking about how to implement that… Whatever the options are, at some point there will be some type of a Factory pattern saying if(Link) { use this presentation }, if (Image) { use that presentation layer.} If you are a smart developer, at this point you may recognize a pattern forming and see the sticky close-coupling situation is being created here. With the hope that the client would not come back again asking for any similar modifications, you commit your code, unsatisfied with the cheap solution, but you didn’t have much choice with the selection of renderings and sublayouts provided.

Now imagine the same situation where each Link-type item in the Links folder knows how to present itself. All the new  “Links List” rendering, which could now be just named generically “List”, would have to do is simply create a list out of items under the specified data source without being concerned about what they are. This is Separation of Concerns at its best – items, presentation, and data are very loosely coupled, making adding an image item to the list as easy as just adding it to the folder with links (it knows how to display itself). The concern of the “List” rendering is now limited to displaying a list of items, without caring about the details of their presentation. Not only this solution is very flexible, it is also greatly reusable, and extendable (tip: types of items hosted in the Links folder can be controlled through the Sitecore Rules).

This is a very simple scenario, however, in complex taxonomies things get much more complicated, and using Item Renderings would make life for all Sitecore developers, and all people that have to deal them by some vice or virtue much easier.

Usability

As we already know the Renderers field is hidden by default and the free entry text field provides much room for error, especially with abstract string like GUIDs. Content Editors start asking questions like, “Do we need braces on each side?”, “Do we need dashes?”, “Should these be capitalized or not?”…etc. All of a sudden you realize that by using the Renderers field you have just created a need for an internal Customer Service department (probably a good thing for the local employment rate)

The usability factor of the Item Rendering was definitely “experiencing a lot of opportunities”.

Customization

Using the Renderers field has eliminated the ability to pass custom parameters to code from the presentation layer, removing that often needed flexibility.

 

The True Sitecore Item Rendering Requirements

Without going any further into what could have possibly been the reason for the handicapped Item Rendering, I created a short list of the following requirements for the True Item Rendering:

  1. The Item Rendering must use the presentation components found in the presentation details of the referenced item

That was it! Once this requirement was satisfied, Content Editors would be able to use the familiar presentation layer tools to render items, and pass parameters and data sources to the renderings, which was in no way possible with the Renderers field.

The True Sitecore Rendering Code

Ok, enough ranting, let’s get to creating a solution-

Fortunately, Sitecore is not a greedy CMS and provides an extensive API, as well as allows you to override most of its functionality.

All we need to do is to create a new TrueItemRenderer inheriting from Sitecore.Mvc.Presentation.ItemRenderer and simply override the GetRenderings() method as shown below –

 

using System.Collections.Generic;
using System.Linq;
using Sitecore;
using Sitecore.Mvc.Presentation;

namespace TrueItemRendering
{
    public class TrueItemRenderer : ItemRenderer
    {
        protected override List<Rendering> GetRenderings()
        {
            var renderings = new List<Rendering>();
            // renderings.AddRange(base.GetRenderings()); // Uncomment to add renderings from the Renderers field (default functionality)

            var refs = Item.Visualization.GetRenderings(Context.Device, false);
            if (!refs.Any())
                return null;

            var renderingReferences = refs.Where(r => !(Context.Database.GetItem(r.RenderingID).TemplateID.ToString() == "{86776923-ECA5-4310-8DC0-AE65FE88D078}" && string.IsNullOrWhiteSpace(r.Settings.DataSource))).ToList();

            if (!renderingReferences.Any())
                return null;

            renderings.AddRange(renderingReferences.Select(r => new Rendering
            {
                RenderingItemPath = r.RenderingID.ToString(),
                Parameters = new RenderingParameters(r.Settings.Parameters),
                DataSource = r.Settings.DataSource
            }));

            return renderings;
        }
    }
}

For the true Sitecore enthusiasts using John West’s Sitecore Helper, we can extend that class by adding TrueItemRendering as one of its methods:

 

 #region

   using System.IO;
   using System.Web;
   using Sitecore.Data.Items;
   using Sitecore.Mvc.Extensions;
   using Sitecore.Mvc.Helpers;
   using Sitecore.Mvc.Pipelines;
   using Sitecore.Mvc.Pipelines.Response.RenderRendering;
   using Sitecore.Mvc.Presentation;

    #endregion

   public static class SitecoreHelperExtension
   {

       /// <summary>
       /// Customs the item rendering.
       /// </summary>
       /// <param name="helper">The helper.</param>
       /// <param name="item">The item.</param>
       /// <param name="parameters">The parameters.</param>
       /// <returns></returns>
       public static HtmlString TrueItemRendering(this SitecoreHelper helper, Item item, object parameters)
       {
           var rendering = GetRendering("Item", parameters, "DataSource", item.ID.ToString());
           rendering.Renderer = new Controls.ItemRenderer
           {
               Item = item
           };

           var stringWriter = new StringWriter();
           PipelineService.Get().RunPipeline("mvc.renderRendering", new RenderRenderingArgs(rendering, stringWriter));
           return new HtmlString(stringWriter.ToString());
       }

       /// <summary>
       /// Gets the rendering.
       /// </summary>
       /// <param name="renderingType">Type of the rendering.</param>
       /// <param name="parameters">The parameters.</param>
       /// <param name="defaultValues">The default values.</param>
       /// <returns></returns>
       private static Rendering GetRendering(string renderingType, object parameters, params string[] defaultValues)
       {
           var rendering = new Rendering { RenderingType = renderingType };
           int index = 0;
           while (index < defaultValues.Length - 1)
           {
               rendering[defaultValues[index]] = defaultValues[index + 1];
               index += 2;
           }
           if (parameters != null)
               TypeHelper.GetProperties(parameters).Each(pair => rendering.Properties[pair.Key] = pair.Value.ValueOrDefault(o => o.ToString()));
           return rendering;
       }
   }

Now we can do fancy stuff like @SitecoreHelper.TrueItemRendering(item, null) in our MVC Views.

To take a step further, we can expose this new Item Renderer to content editors, so they can add it as a rendering to item presentation layer. To do this we will create our own processor and override the existing Sitecore.Mvc.Pipelines.Response.GetRenderer.GetItemRenderer with our logic to call the TrueItemRenderer when Item Rendering is used in Sitecore:

using Sitecore.Diagnostics;
using Sitecore.Mvc.Pipelines.Response.GetRenderer;
using Sitecore.Mvc.Presentation;

namespace TrueItemRendering
{
    public class GetTrueItemRenderer : GetItemRenderer //GetItemRenderer
    {
        public override void Process(GetRendererArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            if (args.Result != null)
                return;
            args.Result = GetRenderer(args.Rendering, args);
        }

        protected override Renderer GetRenderer(Rendering rendering, GetRendererArgs args)
        {
            var itemToRender = GetItemToRender(rendering, args);
            if (itemToRender == null)
                return null;
            return new TrueItemRenderer
            {
                Item = itemToRender
            };
        }
    }
}

And add the following configuration into its own configuration file under \Website\App_Config\Include folder:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <mvc.getRenderer>
        <processor type="Sitecore.Mvc.Pipelines.Response.GetRenderer.GetItemRenderer, Sitecore.Mvc">
          <patch:attribute name="type">TrueItemRendering.GetTrueItemRenderer,TrueItemRendering</patch:attribute>
        </processor>
      </mvc.getRenderer>
    </pipelines>
  </sitecore>
</configuration>

Voila! Now you can enjoy a clean content structure, and code, and all the good karma that comes with it. The ready-to-use True Item Rendering Project can be downloaded here. Simply publish it to your \Website folder and enjoy a True Item Rendering!

14 Comments

Leave a Reply

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

  • Anonymous
    May 23, 2020 at 3:23 am

    In case anyone in the future has the same issue:

    Remove “Controls.” use just “ItemRenderer”

  • Indusa Sd
    February 13, 2017 at 10:38 pm

    Hi John West,

    we have implemented above your code in our solution view file. we have used trueitemrendering.dll and create one handler which is created as above. but in handler we have given error in “rendering.Renderer = new Controls.ItemRenderer” line that “The type or namespace name ‘Controls’ could not be found (are you missing a using directive or an assembly reference?)” can you please help me in this?

  • John West
    August 2, 2016 at 1:07 am

    Maybe could use Name Value Lookup list to select the data template (or an ancestral data template) and specify the class to use for items of that type. Would make more sense to have the lookup on the left and the value on the right.

  • Vasiliy Fomichev
    July 29, 2016 at 8:03 pm

    Hi @disqus_uYmVy9c8r8:disqus ! Good to hear from you! That’s an interesting idea, I do see the value in it. Haven’t thought about it before, but my knee-jerk reaction would be to add a rendering selector field in the Item Rendering’s parameters to specify a custom rendering / sublayout to use to render the item selected in the Datasource field… but I feel like i need to sleep on it 🙂

  • John West
    July 28, 2016 at 5:04 pm

    Hey Vasiliy, and yes this is the real John West. Thanks for posting this and crediting me a little. Thinking about using this for a project. One thought is that we may need different item renderers in different contexts (not just devices), such as when used on one type of page or another. Have you given any thought to that? Never liked the default item renderings stuff – for me, code and changes should be forced through dev and test to prod and does not belong in the database.

  • Vasiliy Fomichev
    May 13, 2016 at 12:04 pm

    cool! thanks for the feedback!

  • Vasiliy Fomichev
    May 13, 2016 at 12:04 pm

    Glad it was helpful @markursino:disqus. Ya, I think the first assumption is likely true. Sometimes architects and BAs overthink things and try to account for scenarios that they perceive to be common, when in reality they are not.

  • Mark Ursino
    April 20, 2016 at 12:57 pm

    I think Sundar may be referring to the HtmlHelper which I think has a typo. Instead of “new Controls.ItemRenderer” it should be “new TrueItemRenderer”

  • Mark Ursino
    April 20, 2016 at 12:52 pm

    So I actually think I might have an idea why Sitecore implemented this with its own special “Renderers” field instead of item presentation. But first, I agree with you that it should use regular presentation settings, the same stuff we know and use all the time. However, here’s an argument for the “special” hidden developer-only field: Well, what’s a page? Merely an item with presentation, no? That means if we add a layout and 0-n renderings on these regular content items, we’re technically making them small pages, no? So I think that was on reason – to keep the true separation between real content pages and supporting content items that may on their own render as pages. The second reason I suspect is the separation of concerns between a content author and developer. I suspect the point of the item renderer was more for a developer to separate the presentation of supporting content items and less for authors to control from a content perspective. Again, just what I suspect. So that’s why they probably made it a hidden text field. If they wanted an author to control it but still separate from regular presentation they could have just made it a droplink, but who knows… Regardless, I agree with you that it really would be nice to use real presentation of an item, or favor that if its set, and fall back to “Renderers” if not. In any case, thanks for this post/solution, its really clever and works really well!

  • Martin English
    April 15, 2016 at 9:28 am

    I know this is an older post, but really great stuff @vasiliyfomichev:disqus. Exactly what I needed for a new project. I did notice that you need to decode your rendering parameters are else they will never get set correctly. Updating this line of code fixed the issue: Parameters = new RenderingParameters(HttpUtility.UrlDecode(r.Settings.Parameters))

  • Vasiliy Fomichev
    April 1, 2016 at 11:53 am

    Hi @disqus_QzyEvLeye5:disqus, I haven’t gone through this exercise in the past, however, I would imagine that you would need to create a new rendering item definition under /sitecore/templates/System/Layout/Renderings add it as an insert option for the rendering folder template ( /sitecore/templates/System/Layout/Renderings/Rendering Folder), modify the config patch above to add a new definition instead of patching the existing one and perhaps look into modifying the mvc.renderRendering pipeline

  • Sundar Ram
    March 28, 2016 at 12:36 am

    @vasiliyfomichev:disqus I want to have both True Item Rendering and Item Rendering. How you suggest to do it. The configuration file patch will replace it.

  • Vasiliy Fomichev
    March 10, 2016 at 8:17 pm

    Hi @disqus_QzyEvLeye5:disqus, the TrueItemRenderer will be used instead of the default ItemRenderer once we apply the configuration file patch described above. The TrueItemRenderer can be used in views with the help of the extension or pulled in dynamically using the Item Rendering in presentation layer.

  • Sundar Ram
    March 4, 2016 at 2:02 am

    Hi, I was looking for something similar to this and found this post. its looks promising for my searching. I need to know how the “TrueItemRenderer” is executed instead of “ItemRenderer”. i.e. in the helper you are calling ” PipelineService.Get().RunPipeline(“mvc.renderRendering”, new RenderRenderingArgs(rendering, stringWriter));” I dont see any code related to calling TrueItemRenderer. Please explain.

%d bloggers like this: