Experience Sitecore ! | More than 200 articles about the best DXP by Martin Miles

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

Welcome Item Reference - a rendering variant field missing out of the box in SXA

Note! The code used in this post can be cloned from GitHib repository: SXA.Foundation.Variants
The majority of folks working with SXA are aware of Reference Item variant field - that allows switching a context of rendering variant from a context item (current page or datasource item, if set) to either one or many items referenced by a link-type field of a given context item. It works like a charm, but in some cases one may meet a case where setting datasource is not applicable or you may need your component to show something different apart from provided datasource item - I have already described how to implement that using Query Variant Field. Let's evaluate it:
Pros
  • it comes out of the box, and could be used straight away
  • you may query in more complex way rather just proving an ID
Cons
  • it relies on SXA search index to be in actual state - you need to have it rebuilt
  • writing query is less user friendly then just picking up an item.
So why not to implement a dedicated Item Reference variant field - it definitely pays off, once used often.

This time I picked up built-in Reference variant field as a donor. Instead of PathTrough field I added Item field of Droptree type:



Code-wise, I implemented one property named PassThroughItem. There's also another one to store child fields, nested underneath given reference item field, those will be executed and render in a switched context:
public class VariantItemReference : BaseVariantField
{
    public string PassThroughItem { get; set; }

    public IEnumerable<BaseVariantField> NestedFields { get; set; }
}
Need to reference IDs of template and its single field:
public static class Constants
{
    public static class RenderingVariants
    {
        public static class Templates
        {
            public static ID ItemReference = new ID("...");
        }

        public static class Fields
        {
            public static class ItemReference
            {
                public static ID Item { get; } = new ID("...");
            }
        }
    }
}
Implement a parser:
public class ParseItemReference : ParseVariantFieldProcessor
{
    public override ID SupportedTemplateId => Constants.RenderingVariants.Templates.ItemReference;

    public override void TranslateField(ParseVariantFieldArgs args)
    {
        ParseVariantFieldArgs variantFieldArgs = args;

        var reference = new VariantItemReference();
        reference.ItemName = args.VariantItem.Name;
        reference.PassThroughItem = args.VariantItem[Constants.RenderingVariants.Fields.ItemReference.Item];

        reference.NestedFields = args.VariantItem.Children.Count > 0
            ? ((IVariantFieldParser)ServiceLocator.ServiceProvider.GetService(typeof(IVariantFieldParser))).ParseVariantFields(args.VariantItem, args.VariantRootItem, false)
            : new List<BaseVariantField>();

        variantFieldArgs.TranslatedField = reference;
    }
}
And a renderer:
public class RenderItemReference : RenderVariantFieldProcessor
{
    public override Type SupportedType => typeof(VariantItemReference);

    public override RendererMode RendererMode => RendererMode.Html;
      
    public override void RenderField(RenderVariantFieldArgs args)
    {
        var control = new PlaceHolder();

        var variantItemReference = args.VariantField as VariantItemReference;
        if (!string.IsNullOrWhiteSpace(variantItemReference?.PassThroughItem))
        {
            var newContextItem = Sitecore.Context.Database.GetItem(new ID(variantItemReference.PassThroughItem));
            if (newContextItem != null)
            {
                foreach (BaseVariantField referencedItem in variantItemReference.NestedFields)
                {
                    RenderVariantFieldArgs variantFieldArgs = new RenderVariantFieldArgs
                    {
                        VariantField = referencedItem,
                        Item = newContextItem,
                        HtmlHelper = args.HtmlHelper,
                        IsControlEditable = args.IsControlEditable,
                        IsFromComposite = args.IsFromComposite,
                        RendererMode = args.RendererMode,
                        Model = args.Model
                    };

                    CorePipeline.Run("renderVariantField", variantFieldArgs);
                    if (variantFieldArgs.ResultControl != null)
                    {
                        control.Controls.Add(variantFieldArgs.ResultControl);
                    }
                }
            }
        }

        args.ResultControl = control;
        args.Result = RenderControl(args.ResultControl);
    }
}
Here is the usage:


Don't' forget to add it into Insert Options and hope you'll enjoy it!

SXA: Implementing URL query parameter rendering variant with little efforts

Note! The code used in this post can be cloned from GitHib repository: SXA.Foundation.Variants

Got a requirement to display a value passed with a URL parameter on a page (URL decoded of course). I want to achieve the goal as quick as possible, with the minimum steps, ideally. So, here we go..

1. Just use existing VariantField (located at /sitecore/templates/Foundation/Experience Accelerator/Rendering Variants/VariantField). When copying, ensure you keep new variant field template outside of Experience Accelerator folder (to avoid it being lost on future SXA upgrade), ideally somewhere within a folder with serialization configured.


2. Remove unwanted fields from a new template. I left only a few I felt necessary, but you may end up having even less. Another thing I've done at this step was adding a user-friendly label to the field, to avoid editors' confusion with a misleading naming:


3. Then you need to create a model. As the simplest, I inherited from VariantField

public class VariantQueryString : VariantField
{
    public VariantQueryString(Item variantItem) : base(variantItem)
    {
    }
}

With a parser, I only process those fields I will be using - what I kept from the previous step: 

public class ParseQueryStringField : ParseField
{
    public override ID SupportedTemplateId => Constants.RenderingVariants.Templates.QueryString;

    public override void TranslateField(ParseVariantFieldArgs args)
    {
        ParseVariantFieldArgs variantFieldArgs = args;

        variantFieldArgs.TranslatedField = new VariantQueryString(args.VariantItem)
        {
            // this property is reused under different purpose rather than named - it stores URL parameter name
            FieldName = args.VariantItem[Constants.RenderingVariants.Fields.QueryField.FieldName],

            Tag = args.VariantItem.Fields[Constants.RenderingVariants.Fields.QueryField.Tag].GetEnumValue(),
            CssClass = args.VariantItem[Constants.RenderingVariants.Fields.QueryField.CssClass],
            Prefix = args.VariantItem[Constants.RenderingVariants.Fields.QueryField.Prefix],
            Suffix = args.VariantItem[Constants.RenderingVariants.Fields.QueryField.Suffix],
            RenderIfEmpty = args.VariantItem[Constants.RenderingVariants.Fields.QueryField.RenderIfEmpty] == "1"
        };
    }
}

Also reference IDs of these fields within Constants class:

public static class Constants
{
    public static class RenderingVariants
    {
        public static class Fields
        {
            public static class QueryField
            {
                public static ID Tag { get; } = new ID("{F556DEDD-5D6B-4FFF-A904-E4C65AB0E698}");
                public static ID CssClass { get; } = new ID("{B3B6B300-1704-493B-999C-AD21CCE58FEF}");
                public static ID FieldName { get; } = new ID("{9D3717EF-CD09-4D98-8C4A-914299503626}");
                public static ID Prefix { get; } = new ID("{5844C4AA-5E48-4721-8E30-91646E430C83}");
                public static ID Suffix { get; } = new ID("{0EFAF48E-E0DD-4408-80D3-4C001101E834}");
                public static ID RenderIfEmpty { get; } = new ID("{E14C3930-FE6C-48AC-99D8-C6867B489066}");
            }
        }
    }
}

4. Finally, implementing RenderQueryStringField class. Nothing complex - just getting a value from the query string, wrapping it with a selected tag/styles and rendering it into the page. Also, there's a fallback scenario for Experience Editor when a given parameter is missing from URL but component needs to be visible.

public class RenderQueryStringField : RenderVariantField
{
    public override Type SupportedType => typeof(VariantQueryString);

    public override RendererMode RendererMode => RendererMode.Html;

    public override void RenderField(RenderVariantFieldArgs args)
    {
        var variantField = args.VariantField as VariantQueryString;
        if (variantField != null)
        {
            args.ResultControl = RenderQueryStringValue(variantField, args);
            args.Result = RenderControl(args.ResultControl);
        }
    }

    protected virtual Control RenderQueryStringValue(VariantField variantField, RenderVariantFieldArgs args)
    {
        var queryString = HttpContext.Current.Request.QueryString;

        if (!string.IsNullOrEmpty(variantField.FieldName) && !string.IsNullOrWhiteSpace(variantField.Tag))
        {
            if (queryString.HasKeys() && queryString[variantField.FieldName] != null)
            {
                var tag = new HtmlGenericControl(variantField.Tag);
                AddClass(tag, (variantField.CssClass + " " + GetFieldCssClass(variantField.FieldName)).Trim());
                tag.InnerText = queryString[variantField.FieldName];
                return tag;
            }

            if (args.IsControlEditable && PageMode.IsExperienceEditorEditing)
            {
                return GetVariantFieldNameLiteral(variantField.FieldName);
            }
        }

        return new LiteralControl();
    }

    protected virtual HtmlGenericControl GetVariantFieldNameLiteral(string parameterName)
    {
        var missingField = new HtmlGenericControl("span");
        missingField.Attributes.Add("class", "missing-field-hint");
        missingField.InnerText = $"[{parameterName}] URL paramenter";
        return missingField;
    }
}

5. I am adding new field variant into existing Foundation project, but if you haven't got one - you might create it. Do not forget to include config patches. something like below:

?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <parseVariantFields>
        <processor type="Platform.Foundation.Variants.Pipelines.VariantFields.QueryString.ParseQueryStringField, Platform.Foundation.Variants" resolve="true" patch:before="processor[@type='Sitecore.XA.Foundation.RenderingVariants.Pipelines.ParseVariantFields.ParseField, Sitecore.XA.Foundation.RenderingVariants']" />
      </parseVariantFields>
      <renderVariantField>
        <processor type="Platform.Foundation.Variants.Pipelines.VariantFields.QueryString.RenderQueryStringField, Platform.Foundation.Variants" resolve="true" patch:before="processor[@type='Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderVariantField, Sitecore.XA.Foundation.RenderingVariants']"  />
      </renderVariantField>
    </pipelines>
  </sitecore>
</configuration>

6. You might consider adding a new variant field into Insert options. Once done, add it as you normally do with variant fields:


Result. That is how it works when passing a URL parameter with a value:


When a parameter is missing from URL, this is how it looks like in Experience Editor, at least making component selectable:


Hope you find this post helpful!

Troubleshooting SXA "Failed to render rendering. Message: An unhandled exception occurred. Source: Sitecore.Mvc"

Got a weird exception, very similar to the one described at StackExchange (SXA Error when open page in Experience Editor). It happened at my CI server, so decided to reproduce locally, deploying on top of a clean instance of Sitecore, and got an issue reproduced.

Here's a call stack:

17264 22:03:56 ERROR Failed to render rendering
Exception: System.Web.HttpUnhandledException
Message: An unhandled exception occurred.
Source: Sitecore.Mvc
   at Sitecore.Mvc.Pipelines.MvcEvents.Exception.ShowAspNetErrorMessage.ShowErrorMessage(ExceptionContext exceptionContext, ExceptionArgs args)
   at Sitecore.Mvc.Pipelines.MvcEvents.Exception.ShowAspNetErrorMessage.Process(ExceptionArgs args)
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain)
   at Sitecore.Mvc.Pipelines.PipelineService.RunPipeline[TArgs](String pipelineName, TArgs args)
   at Sitecore.Mvc.Filters.PipelineBasedRequestFilter.OnException(ExceptionContext exceptionContext)
   at System.Web.Mvc.ControllerActionInvoker.InvokeExceptionFilters(ControllerContext controllerContext, IList`1 filters, Exception exception)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
   at System.Web.Mvc.Controller.ExecuteCore()
   at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
   at Sitecore.Mvc.Controllers.ControllerRunner.ExecuteController(Controller controller)
   at Sitecore.Mvc.Controllers.ControllerRunner.Execute(TextWriter writer)
   at Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args)

Nested Exception

Exception: System.NullReferenceException
Message: Object reference not set to an instance of an object.
Source: Sitecore.XA.Foundation.Grid
   at Sitecore.XA.Foundation.Grid.Commands.ShowGridPropertiesDialog.QueryState(CommandContext context)
   at Sitecore.Shell.Framework.Commands.CommandManager.QueryState(Command command, CommandContext context)
   at Sitecore.Pipelines.GetChromeData.GetChromeDataProcessor.QueryButtonState(WebEditButton button, CommandContext context, String click)
   at Sitecore.Pipelines.GetChromeData.GetChromeDataProcessor.AddButtonToChromeData(WebEditButton button, GetChromeDataArgs args)
   at Sitecore.Pipelines.GetChromeData.GetChromeDataProcessor.AddButtonsToChromeData(IEnumerable`1 buttons, GetChromeDataArgs args)
   at Sitecore.Pipelines.GetChromeData.GetRenderingChromeData.Process(GetChromeDataArgs args)
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain)
   at Sitecore.Pipelines.GetChromeData.GetChromeDataPipeline.Run(GetChromeDataArgs args)
   at Sitecore.Mvc.ExperienceEditor.Presentation.RenderingMarker.GetClientData()
   at Sitecore.Mvc.ExperienceEditor.Presentation.RenderingMarker.get_ClientData()
   at Sitecore.Mvc.ExperienceEditor.Presentation.RenderingMarker.GetStart()
   at Sitecore.Mvc.ExperienceEditor.Presentation.Wrapper..ctor(TextWriter writer, IMarker marker)
   at Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper.Process(RenderRenderingArgs args)
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain)
   at Sitecore.Mvc.Pipelines.PipelineService.RunPipeline[TArgs](String pipelineName, TArgs args)
   at Sitecore.Mvc.Pipelines.Response.RenderPlaceholder.PerformRendering.Render(String placeholderName, TextWriter writer, RenderPlaceholderArgs args)
   at (Object , Object )
   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists)
   at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain)
   at Sitecore.Mvc.Pipelines.PipelineService.RunPipeline[TArgs](String pipelineName, TArgs args)
   at Sitecore.Mvc.Helpers.SitecoreHelper.RenderPlaceholderCore(String placeholderName, TextWriter writer)
   at Sitecore.Mvc.Helpers.SitecoreHelper.Placeholder(String placeholderName)
   at ASP._Page_Views_Shared_Partial_Design_Dynamic_Placeholder_cshtml.Execute() in c:\inetpub\wwwroot\RssbPlatform.dev.local\Views\Shared\Partial Design Dynamic Placeholder.cshtml:line 5
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
   at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
   at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)

No single reference to my code, or whatsoever prompting to the actual reason.

Troubleshooting this was not an easy walk out. Logs also contained nothing relevant to the issue. Since I always keep at least daily backups done with a helpful tool Sif0n, it was relatively easy to substitute a web root folder from a fully functional solution. but the problem persisted. Then it is likely to be caused by something stored in a database.

I suspected something could be wrong with my serialization, I checked the settings but those were all correct. Then I restored fully functional database along with existing instance, calling it master_OK and started comparing those items one by one. And finally, I found the difference:


Reason for error: Rendering parameters template was missing due not being serialized and synced on that new environment. How could that happen? Don't know for sure, but a very alike suspect is that when using a clone rendering SXA script I accidentally missed out secondary options, so that script cloned rendering parameters for a new rendering, but left it underneath Experience Accelerator folder. Which is kept outside of serialization (as I more or less correctly predicted at the beginning of my investigation). So rendering parameters template was referenced, but did not exist on the target system. 

After fixing it, the site worked well. I do understand that the call stack I included above and the error messages are quite generic and may be met but any other scenarios, so probably Sitecore needs to assert and explicitly log such errors. At least I shared the way of dealing with it and my exact reason with you, hope it may help someone.

What is Query Variant Field to be used with rendering variants and few real life scenarios of using it

Imagine a case - there is a datasource item, that has a Droplink field to pointing to another item, let's say a Rich Text with an address (as on an image below):


That means, in your rendering variant you simply use Reference Variant Field referencing an item behind Rich text droplink, and within that Rich text item you simply render a field, as normal:


Seems obvious, right? Let's increase complexity!


Scenario one

We change scenario so that Droplink would now point not to an item having the field to render, but rather to an item, containing other children you might need to display:


So instead of referencing a Rich text item, we'll reference Link list item, where you need to iterate child items (links) of a referenced Link List item, and show all these links on a page with a rendering variant:


Question: how do I iterate children of my referenced item?

Okay, that's where your new friend comes to help you. Welcome, Query Rendering Variant Field!

What it actually does is also switching a context to a single item / multi items, similarly to Reference Variant Field, but instead of taking switching context from a link-powered field, it uses Sitecore search query and switches context to its execution result. Here's how one can add Query Variant Field into a rendering variant:


So for my simple example with Link List children, I use a query to search all the children of a current item (Link List):


That does the job and withing this query item you may use fields related to items coming out from a search.

One more helpful tip: when working with references, queries or whatsoever changes the context, you would often wondering iа changing context worked well and referencing what you intend to. The trick I use is inserting a debugging template field exposing ID and/or a name of where we've switched to (or any other universal property all the items do have and that are accessible from NVelocity $item):


This takes only 5 seconds of my time to implement but immediately helps to visualise a context on output. Very helpful!


Second scenario

... where Query Field Variant helps me so much. I need to implement a Subscription Level identifier on every page, so that it shows which privilege a current user has (obviously, he/she will see an only single one of the labels show below at one time):


But there is a requirement for that labels itself to be changeable and kept somewhere under site's settings item - subject to alterations at a later stage. So I already have a separate folder with such items:


With using a Reference Variant Field one would need to have rendering having a datasource item with a field pointing to one of those subscription items, which is not an elegant scenario. And what if my rendering does not accept datasources? Or it simply breaks sense, as in a described scenario?

Since we know the IDs of these subscription items we can reference them directly and that's where Query Variant Template also helps us. Also, search query by an ID always returns not more than one item, and since we know item exists we can use it as direct linking. That's how I do that:


Works like a charm!

I have just shown only two real-life examples of using Query Variant Field, but the potential area of their usage is virtually unlimited - as much as you can query you indexes. This gives an ultimate tool for comfy switching context as much as you may need that, but I'd warn you from overusing it too much.

How to make a link to downloadable media item from within SXA rendering variant?

This is just a quick tip about the way you can create a downloadable link to some media item from within a rendering variant. Given, that you have a template with a field of type File, and an item of that template has a downloadable resource, let's say PDF. Trying to use that field worn't work normally.

What you should do instead is create a reference variant field to that media item, attached within a File field. And from within a context of that reference variant field, you address to a field called File path. The image below describes that trick in action:


Hope that trick helps!

Creating custom SXA components with rendering variants and (almost) no codebehind on an example of social share buttons

Introduction. Initially, I was going to implement social share buttons on my page. The first thing coming into my mind was to use existing share components coming OOB with SXA. That's what I did and it looked well.. until I viewed generated source. It looks quite monstrous, includes iframes, inline JavaScripts and in general brought customization issues for my front-end developers. Here's how 2 of 3 buttons look like when rendered (3-rd is collapsed):


In general, that looks sort of over-engineering for given requirements. The code suggested by my front end developers looked more-less elegant like this:

<div class="social-block">
	<span class="social-block__label">share this</span>
	<ul class="social-block__list">
		<li class="social-block__item">
			<a href="#" class="social-block__link social-block__link_facebook"></a>
		</li>
		<li class="social-block__item">
			<a href="#" class="social-block__link social-block__link_twitter"></a>
		</li>
		<li class="social-block__item">
			<a href="#" class="social-block__link social-block__link_linkedin"></a>
		</li>
		<li class="social-block__item">
			<a href="#" class="social-block__link social-block__link_mail"></a>
		</li>
	</ul>
</div>

Definitely much nicer and cleaner, given that sharing functionality provided by many social platforms can be implemented just in a form of a link (ie. //www.facebook.com/sharer/sharer.php?u=https://your-site/page-to-share), so that can be simply passed with href attribute of anchor tag. 


Implementation. Based on that, I decided to implement my own Share buttons component. Of course, unwilling to create a new component from a scratch, I decided to take a look on existing with a purpose of cloning it. There were 2 requirements while picking the right component to clone: 1) it should support rendering variants and 2) it should work with datasource template, so that those are also cloned. It looks that Promo component fits the purpose well, so here we go, by using an SXA built-in clone script available from a context menu:



By that moment, I already have my custom module named Components, sitting outside of Experience Accelerator folder, that will likely be overridden by next version update, and where I separate my custom controls. So, in the following dialog, name the component, state where you want to put it and also give the name to CSS class to be used with it. 

I called my new component Social buttons, so that I will make two rendering variant as the part of it: one for sharing current page over social networs, and another will be called Social presense simply leading to my company's social network accounts. I also named CSS class social-buttons:

Also, in order to be able to provide some defaults, I also make a copy of Promo's rendering parameters under a new name, so that I could adjust it later according to my requirements:

And also copying datasource:

And finally, to make my new component even more isolated from its donor, I copy the rendering view file as well:


It is not mandatory, you may keep using existing control rendering. However, In my case I do want to modify HTML structure, to get rid of nested <div class="component-content"> elements, so I select "Copy MVC view file (specify path below)" option. 

Note 1: before you start cloning component, make sure ~/Views/SocialButtons target folder exists, otherwise script errors out. Also, once done don't forget to copy cloned rendering into your working folder so that you include it into solution and add to the source control (somewhere similar to src/Feature/Components/code/Views/SocialButtons/SocialButtons.cshtml as in my case).

Note 2: just to remind, that rendering view, CSS class name for the component and other settings can be adjusted later at the Experience Accelerator section of rendering item:

Click Proceed and when complete, you see successful result confirmation:


Once clone script finished, we may also want to add new component into the toolbox. To do so, navigate to Presentation - Available Renderings, find the desired module and add a new component into it:

I made few more changes - renamed Social buttons into Social buttons group as it makes more sense, and made item and folder templates for buttons. 

Here's what I got under feature templates for Social buttons group:


and for Social button:

You may also notice two correspondent folder templates to create folders for storing items of these new types.

These were feature-layer templates, but there's one more project-layer interface template to be appended to page types inheritance:


So now, under Data folder I can create social buttons and a social share group based on (some of) these buttons. I made just one and called it Default


From this moment, adding _Social share groups interface template into an inheritance of some page templates adds a section with an option of selecting a social group for all pages of these type. You may also customize label (and if) shown next to social buttons.


Usage. Here we carry on. After adjusting placeholder settings, you added a new component to a page, then selected appropriate datasource but nothing appears as the result. This comes due to a component supporting rendering variants, does not have any single variant availability. How comes that? 

Note 3: the clone script does not clone rendering variants (at least per SXA version 1.8), so you need to create at least one. Remember that when creating new Variants item (or duplicating an existing one), the name should be the same, as the name of the corresponding rendering:


Insert at least one Variant Definition. 

Note 4: the way rendering variants work it the first Variant Definition in the list becomes default one, if not set explicitly. I called variant Share buttons and added Info field so that it becomes visible on partial design in Experience Editor straight away (I explain Info field trick in more details in this article).


At least component shows up in Experience Editor as:


Now being aware that right component with right rendering variant presents on a page, I can carry on implementing that variant according to my desired output. 

Note 5: these buttons are not subjects to changes by content editors from within EE, rather they are configured by the site setup. That's why links should not be editable so I can use template variant field in order to render anchor tag precisely as per our requirements.


Creating rendering variant. This is how I implemented Share buttons rendering variant:


Few things I'd like to explain about the way it is structured and how it works. 

1. Naming conventions. instead of giving sections generic names, I am following "Element - Class" approach. Of course, it would make much sense from interpreting the point of view to name them "Element.Class" as CSS selectors normally do, however using dot is not allowed to be used for Sitecore item names by default while explicit setting that value for display name is an exhaustive waste of time. In any case, it is clear what element renders to.

2. You may notice, that I am having two reference variant fields, one nested into another. The way it works is that it contains the name of a field from a context item, that has a link to another item (or items). Thus, it switched context so that all nested field will be executed in the context of that "proxied" item.

3. NVelocity template (pictured above) is the very powerful way to extend rendering variants functionality far beyond out-of-the-box variant fields, employing the power of custom C# code, pre-built tools or creating your own. In one of my previous posts, I mentioned SXA built-in token tools to be used with rendering variant templates, so I am using the one already have in my solution, but if you haven't - these 2 snippets would be the only piece of your C# code to implement. Link Tool:

public class LinkTool
{
    public static string GetItemLink(Item item, bool includeServerUrl = false)
    {
        var options = UrlOptions.DefaultOptions.Clone() as UrlOptions;

        if (includeServerUrl)
        {
            options.AlwaysIncludeServerUrl = true;
        }

        return LinkManager.GetItemUrl(item, options);
    }
    public static string GetCurrent(bool includeServerUrl = false)
    {
        var item = Sitecore.Context.Item;
        return item == null ? String.Empty : GetItemLink(item, includeServerUrl);
    }
}
and ItemFieldTool below:
public class ItemFieldTool
{
    public static string GetField(Item item, string fieldName)
    {
        ReferenceField field = item.Fields[fieldName];
        return field?.Value ?? String.Empty;
    }
}

The payload, however, is lack of ability to edit anchor tag, as I mentioned in Note 5 above. But, that should not surprise, given that you see that it is being calculated on a fly from different sources. GetFieldTool reads field value of a context item, where context has been changed twice already, in this case, a variant is iterating through the list of Social buttons and $item relates to Social button item.

#set($title = $itemFieldTool.GetField($item, "Title"))
#set($suffix = $itemFieldTool.GetField($item, "Style suffix"))
#set($prefix = $itemFieldTool.GetField($item, "Url prefix"))
But how do I get a something from the context of an initial item, before "tunnelling" two times through references? The code behind tools is being still executed in Sitecore.Context which means access to page item, so I implemented GetCurrent() method that returns full URL of context page. I use it for appending to URL prefix of a social button.
$linkTool.GetCurrent(true)


Getting result. Finally, after opening exact item in Experience Editor (and not the partial design edited previously that does not wire to actual data), I am getting expected share links rendered, with correct URL:


and after applying CSS styles on a published site, Info Field scaffolding is not shown and we see nicely applied social share buttons:


This is a flexible approach allowing to configure individual buttons set per each page template by standard values with an option of overriding the default for specific page individually.

That was a walkthrough of creating new components with (almost) no new backend code written. You learned how to create custom components by cloning existing, how to create rendering variants, how to use reference fields to "proxy" the context and NVelocity templates from within rendering variant.

Hope it helps!

SXA tip: use information scaffolding fields to get more visibility over your components in Experience Editor

When working in Experience Editor, by default you see empty fields of components, that allows you editing these fields, selecting hierarchy and thus modifying components. However not always that happens...

Scenario: creating a rendering variant that has VariantReference (for switching context of related item) while editing partial design in Experience Editor. 

Symptoms: what actually happens is that partial designs are edited in their own context, all presentation data you create is stored in Final Renderings for that particular Partial Design, and not the page item. It will pull data from page item only once applied that item or (more likely) associated with its page template. But when using VariantReference field, it won't be wired to any data, thus not proxying the context to anything, leading to all the fields underneath VariantReference field item not rendered in EE. If there are not any other visuals within given rendering variant - entire component becomes non-selectable in EE.

Suggestion: to create some information field to be shown in Experience Editor, so that you may select given component and adjust its settings, as you normally do. At the same time we don't want this field to present outside of Experience Editor since its only purpose is make editors aware about this component.

Solution: that's the case where we can use personalization with a custom "where the Experience Editor is in editing mode" condition I recently wrote about. Having it in scope trick goes very simple - simply create a text variant field and enter component and rendering variant info: 


And once done, create a personalization rule for that info field to be shown only in Experience Editor, using newly created condition:


Finally, instead of unclickable whitespace, you'll see the component:


Hope this basic trick helps improving your productivity while working with SXA!

I have been awarded the Sitecore Most Valuable Professional Technology Award in 2019 - Sitecore MVP

Just got an email with absolutely positive news. It stated:

                

Great news, Sitecore! 

That is always a pleasure to realise that you are helpful to others, and your efforts were spent for good. 

In my opinion, the two most interesting bonus of being an MVP are early preview of new Sitecore platform and its parts and taking part in annual MVP Summit, taking place along with Symposium, where you can actually talk to all these people who build the platform and get their internal vision, discuss features and roadmap, give feedback and report some bugs (if any). Ah, they also sent me this badge, so that I can append it to blog or some other projects:


Last, but not the least benefit, is to be a part of such an enthusiastic community, among the best and smartest, among these people (see the list of the winners grouped by countries):



This is my third MVP award in a row, however, will keep working hard, hopefully, get one more next year. 

Creating a custom rendering variant section to render element with background image

Note! The code used in this post can be cloned from GitHib repository: SXA.Foundation.Variants

I am working on a rendering variant, and was looking on getting an element with background image from Sitecore, something similar to this:

<div style="background-image: url(/-/media/Project/Tenant/Platform/Some-image-from-Sitecore.jpg)">
    <div>... some rendering variant fields here</div>
</div>
There is way to achieve that by placing container rendering, as it has an option of picking up a background image:


However, even it may bring desired result, having container it is not the best and simplest from structuring point of view as it creates plenty of nesting element and image comes not at the top of them, not to say that it is not editors friendly and have to mess with placeholder settings. ideally, I wanted something like Section which in fact is just a div container to store other variant fields, but with style attribute stating background image. Okay, since I found nothing, I can implement that with a new variant field.

Since Section seems to be the best option, let's just clone and extend it. To start with, I copy VariantSection template into my tenant templates folder under foundation, and name it Image Section. I add just one ImageFieldName field of type image to the newly created template, that will be a reference to background image. So far, so good.

Then I run /sitecore/admin/showconfig.aspx in order to investigate what logic is involved into processing sections and find these 2 processors, one for parser:

<parseVariantFields patch:source="Sitecore.XA.Foundation.RenderingVariants.config">
  <processor type="Sitecore.XA.Foundation.RenderingVariants.Pipelines.ParseVariantFields.ParseSection, Sitecore.XA.Foundation.RenderingVariants" resolve="true"/>
</parseVariantFields>
and another - for renderer:
<renderVariantField patch:source="Sitecore.XA.Foundation.RenderingVariants.config">
  <processor type="Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection, Sitecore.XA.Foundation.RenderingVariants" resolve="true"/>
</renderVariantField>

With JetBrains dotPeek, it becomes fairly easy to find the code and extend it. Based on what I found, I create 3 new classes to implement parser, renderer and also the model itself:


I extend section variant field with one extra Single-Line Text field, stating which field from the context item should contain image. Also, I am referencing my new template created earlier. Here's the code:

using System.Collections.Generic;
using Sitecore.Data.Items;
using Sitecore.XA.Foundation.Variants.Abstractions.Fields;
using Sitecore.XA.Foundation.RenderingVariants.Fields;

namespace Platform.Foundation.Variants.Pipelines.VariantFields
{
    public class VariantImageSection : RenderingVariantFieldBase
    {
        public VariantImageSection(Item variantItem) : base(variantItem)
        {
        }

        public static string DisplayName => "Image Section";

        public IEnumerable<BaseVariantField> SectionFields { get; set; }

        public string ImageFieldName { get; set; }

        public string LinkField { get; set; }

        public bool IsLink { get; set; }
    }
}

and now implement parser. Please pay attention that since we duplicated the template, fields for Image section will have new IDs, so we will also re-reference Tag, CssClass and IsLink fields from our static constants class, where we keep all the IDs:

using System.Collections.Generic;
using Sitecore.Data;
using Sitecore.DependencyInjection;
using Sitecore.XA.Foundation.SitecoreExtensions.Extensions;
using Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.ParseVariantFields;
using Sitecore.XA.Foundation.Variants.Abstractions.Services;

namespace Platform.Foundation.Variants.Pipelines.VariantFields
{
    public class ParseImageSection : ParseVariantFieldProcessor
    {
        public override ID SupportedTemplateId => Constants.RenderingVariants.Fields.VariantImageSection;

        public override void TranslateField(ParseVariantFieldArgs args)
        {
            ParseVariantFieldArgs variantFieldArgs = args;

            var variantSection = new VariantImageSection(args.VariantItem);
            variantSection.ItemName = args.VariantItem.Name;
            variantSection.Tag = args.VariantItem.Fields[Constants.RenderingVariants.Fields.Tag].GetEnumValue();
            variantSection.CssClass = args.VariantItem[Constants.RenderingVariants.Fields.CssClass];
            variantSection.ImageFieldName = args.VariantItem[Constants.RenderingVariants.Fields.ImageField];
            variantSection.LinkField = args.VariantRootItem[Sitecore.XA.Foundation.Variants.Abstractions.Templates.IVariantDefinition.Fields.LinkField];
            variantSection.IsLink = args.VariantItem[Constants.RenderingVariants.Fields.IsLink] == "1";

            variantSection.SectionFields = args.VariantItem.Children.Count > 0 
                ? ((IVariantFieldParser)ServiceLocator.ServiceProvider.GetService(typeof(IVariantFieldParser))).ParseVariantFields(args.VariantItem, args.VariantRootItem, false) 
                : new List<Sitecore.XA.Foundation.Variants.Abstractions.Fields.BaseVariantField>();

            variantFieldArgs.TranslatedField = variantSection;
        }
    }
}

Now we implement renderer logic. What I am doing below, is getting a field name of a context item, where image supposed to be, and then generate a URL for that media item (in case field is valid and media item indeed presents there), further down this URL is being appended our section element (remember, you may select any tag, not just div) as a background image style attribute:

using System;
using System.Web.UI.HtmlControls;
using Sitecore.Data.Fields;
using Sitecore.Pipelines;
using Sitecore.Resources.Media;
using Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField;
using Sitecore.XA.Foundation.Variants.Abstractions.Fields;
using Sitecore.XA.Foundation.Variants.Abstractions.Models;
using Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.RenderVariantField;

namespace Platform.Foundation.Variants.Pipelines.VariantFields
{
    public class RenderImageSection : RenderRenderingVariantFieldProcessor
    {
        public override Type SupportedType => typeof(VariantImageSection);

        public override RendererMode RendererMode => RendererMode.Html;

        public override void RenderField(RenderVariantFieldArgs args)
        {
            string styleInlineValue = String.Empty;
            var variantField = args.VariantField as VariantImageSection;

            if (!string.IsNullOrWhiteSpace(variantField?.ImageFieldName) && args.Item != null)
            {
                ImageField imgField = args.Item.Fields[variantField.ImageFieldName];
                if (imgField != null)
                {
                    string url = MediaManager.GetMediaUrl(imgField.MediaItem);
                    styleInlineValue = $"background-image: url({url})";
                }
            }

            var tag = new HtmlGenericControl(string.IsNullOrWhiteSpace(variantField.Tag) ? "div" : variantField.Tag);

            if (styleInlineValue.Length > 0)
            {
                tag.Attributes.Add("style", styleInlineValue);
            }

            AddClass(tag, variantField.CssClass);
            AddWrapperDataAttributes(variantField, args, tag);

            foreach (BaseVariantField sectionField in variantField.SectionFields)
            {
                var variantFieldArgs = new RenderVariantFieldArgs
                {
                    VariantField = sectionField,
                    Item = args.Item,
                    HtmlHelper = args.HtmlHelper,
                    IsControlEditable = args.IsControlEditable,
                    IsFromComposite = args.IsFromComposite,
                    RendererMode = args.RendererMode,
                    Model = args.Model
                };

                CorePipeline.Run("renderVariantField", variantFieldArgs);

                if (variantFieldArgs.ResultControl != null)
                {
                    tag.Controls.Add(variantFieldArgs.ResultControl);
                }
            }

            args.ResultControl = variantField.IsLink ? InsertHyperLink(tag, args.Item, variantField.LinkAttributes, variantField.LinkField, false, args.HrefOverrideFunc) : tag;
            args.Result = RenderControl(args.ResultControl);
        }
    }
}

Once done, create a patch configuration file and reference new processors: parser and renderer:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <parseVariantFields>
        <processor type="Platform.Foundation.Variants.Pipelines.VariantFields.ParseImageSection, Platform.Foundation.Variants" resolve="true"/>
      </parseVariantFields>
      <renderVariantField>
        <processor type="Platform.Foundation.Variants.Pipelines.VariantFields.RenderImageSection, Platform.Foundation.Variants" resolve="true"/>
      </renderVariantField>
    </pipelines>
  </sitecore>
</configuration>

The code above requires you to reference Sitecore.XA.Foundation.RenderingVariants, Sitecore.XA.Foundation.SitecoreExtensions and Sitecore.XA.Foundation.Variants.Abstractions libraries.

Another thing you will need to provide is adding insert options. I do not cover it as it is quite obvious. Finally, you'll be able to use your new rendering variant field:


with


which renders the following output and allows child / nested elements, as normal section would do:


This explains how one can easily extend existing rendering variant fields and provide any custom output without lengthy process of creating a custom component in SXA.

Rule missing OOB in Sitecore, where the Experience Editor is in editing mode

I was playing with SXA and wanted to have a custom element within a rendering variant that identifies current control presents on a page, otherwise it is being invisible. Looking for such a rule I suddenly realised it is not there, so had to create my custom one. I am sharing it just in case it might be useful to someone looking for a similar solution.

So, as per Rules Engine Cookbook, you create a tag, custom condition and relevant element for it:


The code is simple.

public class IsExperienceEditorEditing<T> : StringOperatorCondition<T> where T : RuleContext
{
    public int Value { get; set; }
    protected override bool Execute(T ruleContext)
    {
        return Sitecore.Context.PageMode.IsExperienceEditorEditing;
    }
}

Also, working with SXA aligned with Helix principles, it would make sense to create a foundation module for custom rules, drop the above class inside as well as serialization for newly created tag and elements:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:unicorn="http://www.sitecore.net/xmlconfig/unicorn/">
  <sitecore unicorn:require="On">
    <unicorn>
      <configurations>
        <configuration name="Foundation.Rules" description="Foundation Rules" extends="Helix.Foundation">
          <predicate type="Unicorn.Predicates.SerializationPresetPredicate, Unicorn" singleInstance="true">
            <include name="$(layer).$(module).Tags" database="master" path="/sitecore/system/Settings/Rules/Definitions/Tags/Experience Editor" />
            <include name="$(layer).$(module).Elements" database="master" path="/sitecore/system/Settings/Rules/Definitions/Elements/Experience Editor" />
          </predicate>
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>
So now I can have an element withing rendering variant that is shown only when in editing mode:

Hope you find this helpful!