Wednesday 22 December 2021

How to switch betwen custom variants (CSHTML) when using SXA

How to switch between custom CSHTML variants in when using SXA

With SXA comes the ability to create rendering variants, but each of the variants has to have its HTML defined as items in Sitecore and you have to use the SXA VariantsController (or inherit it).  But what-if you want to use your own cshtml files and have variants of those?  How do you get Sitecore to switch them?

You will need to go ahead and create a controller (make sure to inherit: Sitecore.XA.Foundation.Mvc.Controllers.StandardController), the views for each of your variants, and a rendering item in Sitecore.  - You need to go ahead and set up your rendering item just as you would for a custom component.
N.B. This article doesn't cover how to wire up the data-source in the controller; but I do have an article that talks about how to create a rendering model using GlassMapper, that follows the same pattern as an SXA rendering model (Sitecore.XA.Foundation.Mvc.Models.RenderingModelBase), you can read that here: Extending SXA.  But that's another story...

After you have created all of those pieces go into Sitecore and navigate to the Rendering Variants for your site, and then insert a Variants Item, giving it the collective name for your variants (E.g, Hybrid Column).  Make sure to update the Compatible Renderings field to include the new Sitecore rendering you just created in Layout/Renderings.

Now right click on the newly created Variants item and insert a Variant Definition item for each of the views you created.  For example:

Note: The Variant Definition items do not have any child items - I.e no HTML structure.

You will now need to make a note of the Item ID for each of the Variant Definition Items.

In the controller you created, you will now be overriding the GetIndexViewName method.  In here you will find the ID of the variant selected and based on that set the path (ViewName) to the view you created for that variant.

Here are the constants I set up for the above example:

private const string FieldNames = "FieldNames";
private const string FourColumnVariantId = "{363B93FC-86D0-42E5-8176-EF843C4697D0}";
private const string FourColumnGreenVariantId = "{B9A250BC-91B9-4BE8-903B-9EEBE31FAA9E}";
private const string ThreeColumnVariantId = "{74B1E806-B58F-4DEE-A423-13AF62550536}";
private const string ThreeColumnGreenVariantId = "{1CD230A6-A5C3-4F24-B394-FE0367863B51}";
private const string TwoColumnVariantId = "{B764C891-F8C2-4714-A27A-7ADF466DCEC5}";
private const string FourColumnView = "~/Views/Project/HybridColumn/FourColumn.cshtml";
private const string ThreeColumnView = "~/Views/Project/HybridColumn/ThreeColumn.cshtml";
private const string TwoColumnView = "~/Views/Project/HybridColumn/TwoColumn.cshtml";

Now we override the GetIndexViewName method and find the selected rendering variant in the Rendering.Parameters property, and return the appropriate view path.  And that's it!

        protected override string GetIndexViewName()
            if (Rendering?.Parameters?[FieldNames] == null) return base.GetIndexViewName();
            switch (Rendering.Parameters[FieldNames])
                case FourColumnVariantId:
                case FourColumnGreenVariantId:
                    return FourColumnView;
                case ThreeColumnVariantId:
                case ThreeColumnGreenVariantId:
                    return ThreeColumnView;
                case TwoColumnVariantId:
                    return TwoColumnView;
                    return base.GetIndexViewName();