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!