SXA: Create custom NVelocity template renderer

One of the field types for display with Rendering Variants is Template. It allows you to define your own NVelocity template to embed custom processing of the output. The really cool thing about it that it can be made flexible even further and extended by adding custom renderers.

The templates mentioned above are processed by getVelocityTemplateRenderers SXA pipeline outlined here. That gives us a trace that this could be achieved with extending the pipeline by adding a custom AddTemplateRenderers processor. Taking a closer look with ILSpy at Sitecore.XA.Foundation.Variants.Abstractions.dll reveals that we can do that by implementing the IGetTemplateRenderersPipelineProcessor.

SXA docs for Creating a Rendering Variant available here describe a predefined set of objects and tools provided with SXA to use in the template:

  • $item: access to the current item ($item.Name displays current item name).
  • $dateTool.Format(date, format): formats date and time values.
  • $numberTool.Format(number, format): formats numbers.
  • $geospatial: in case of geospatial search ($geospatial.Distance) will show distance to certain point of interest).


What's really cool, you can easily extend this list and implement a custom renderer with business logic of your choice. Some good usage examples could be your company's real-time share price or current weather in your location. However, as Sitecore community is well known for it's outstanding sense of humour we need to keep up - let's create a tool rendering a random joke for display.

First, to locate where to hook our implementation, let's have a look into the getVelocityTemplateRenderers pipeline:

<getVelocityTemplateRenderers patch:source="Sitecore.XA.Foundation.Variants.Abstractions.config">
  <processor type="Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.GetVelocityTemplateRenderers.InitializeVelocityContext, Sitecore.XA.Foundation.Variants.Abstractions" resolve="true"/>
  <processor type="Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.GetVelocityTemplateRenderers.AddTemplateRenderers, Sitecore.XA.Foundation.Variants.Abstractions" resolve="true"/>
</getVelocityTemplateRenderers>

 

Based on the decompiled implementation of AddTemplateRenderers processor, we just need to inject another entry into the pipeline context. This should do the trick:

using Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.GetVelocityTemplateRenderers;

namespace Sitecore91.Foundation.SXAExtensions.Pipelines.NVelocityTemplateRenderers
{
    public class AddCustomTemplateRenderers : IGetTemplateRenderersPipelineProcessor
    {
        public void Process(GetTemplateRenderersPipelineArgs args)
        {
            args.Context.Put("jokeTool", new JokeTool());
        }
    }
}

 

Our implementation of JokeTool consumes the Geek Jokes API to provide us with a random joke on each pipeline execution (at least as long as it's not cached):

using System;
using System.Net.Http;

namespace Sitecore91.Foundation.SXAExtensions.Pipelines.NVelocityTemplateRenderers
{
    public class JokeTool
    {
        public string GetRandomJoke()
        {
            var jokeApiUrl = "https://geek-jokes.sameerkumar.website/api";
            try
            {
                using (var httpClient = new HttpClient())
                {
                    return httpClient.GetStringAsync(jokeApiUrl).GetAwaiter().GetResult();
                }
            }
            catch (Exception e)
            {
                return "No jokes today.";
            }
        }
    }
}

 

Having code in place, let's wire it up with an appropriate patch in the end of pipeline:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getVelocityTemplateRenderers>
        <processor type="Sitecore91.Foundation.SXAExtensions.Pipelines.NVelocityTemplateRenderers.AddCustomTemplateRenderers, Sitecore91.Foundation.SXAExtensions" 
                   patch:after="processor[@type='Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.GetVelocityTemplateRenderers.AddTemplateRenderers, Sitecore.XA.Foundation.Variants.Abstractions']" />
      </getVelocityTemplateRenderers>
    </pipelines>
  </sitecore>
</configuration>

 

What should result in amending the pipeline as below:

<getVelocityTemplateRenderers patch:source="Sitecore.XA.Foundation.Variants.Abstractions.config">
  <processor type="Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.GetVelocityTemplateRenderers.InitializeVelocityContext, Sitecore.XA.Foundation.Variants.Abstractions" resolve="true"/>
  <processor type="Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.GetVelocityTemplateRenderers.AddTemplateRenderers, Sitecore.XA.Foundation.Variants.Abstractions" resolve="true"/>
  <processor type="Sitecore91.Foundation.SXAExtensions.Pipelines.NVelocityTemplateRenderers.AddCustomTemplateRenderers, Sitecore91.Foundation.SXAExtensions" patch:source="AddCustomTemplateRenderers.config"/>
</getVelocityTemplateRenderers>

 

To see in action our newly created tool, we use it with the Template field just like the other already predefined ones (e.g. $dateTool):

 

Now you can enjoy your day with a fresh portion of wisdom on each page visit:

That was literally fun.