Tuesday, October 24, 2023

Sitecore SXA Responsive Image - null reference

 SXA Responsive Image null reference issue

We recently faced an issue on Sitecore SXA with the Responsive Image component. Our customer informed us that if an image was not published pages containing that image would be broken. And not just missing the image - but actually broken. 

The site is running on Sitecore 10.2 using SXA - and the site is patched with the latest hotfix packages so actually running on 10.2.1 (and even 10.2.2 now). On the broken pages we had renderings that use the ootb Responsive Image rendering variant. So it was clear that the issue was with that component.

In the release notes for version 10.3 we found the matching bug: 
If a Responsive Image variant field points to a broken link, a NullReferenceException error occurs.
Upgrading is 10.3 is not an option. So let's try to fix this ourselves. I found the issue in the class Sitecore.XA.Feature.Stylelabs.Pipelines.RenderVariantField.RenderFallbackResponsiveMImage which came with the 10.2.1 update. The RenderField method in that class uses a function called StylelabsDataExtractor.GetSrcAttributeValue. That is weird as this one is deprecated in 10.2 and it mentions to use the new function GetAttributeValue. When I tried the same code with that function the error was gone. 

So let's recap that and show the code you need.
using System.Web.UI;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Web.UI.WebControls;
using Sitecore.XA.Feature.Stylelabs.Services;
using Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.RenderVariantField;

namespace Foundation.Infrastructure
{
    public class RenderFallbackResponsiveMImage : Sitecore.XA.Feature.Stylelabs.Pipelines.RenderVariantField.RenderFallbackResponsiveMImage
    {
        public RenderFallbackResponsiveMImage(IStylelabsDataExtractor stylelabsDataExtractor) : base(stylelabsDataExtractor)
        {
        }

        public override void RenderField(RenderVariantFieldArgs args)
        {
            var variantField = args.VariantField;
            if (args.Item.Paths.IsMediaItem || string.IsNullOrWhiteSpace(variantField?.FieldName))
            {
                base.RenderField(args);
            }
            else if (PageMode.IsExperienceEditorEditing)
            {
                args.ResultControl = (Control)new FieldRenderer()
                {
                    Item = args.Item,
                    FieldName = variantField.FieldName,
                    DisableWebEditing = !args.IsControlEditable
                };
                args.Result = this.RenderControl(args.ResultControl);
            }
            else
            {
                var field1 = args.Item.Fields[variantField.FieldName];
                if (field1 == null)
                {
                    return;
                }

                if ((FieldTypeManager.GetField(field1) is ImageField field2 ? field2.MediaItem : (Item)null) == null)
                {
                    field2 = (ImageField)field1;
                    string srcAttributeValue = this.StylelabsDataExtractor.GetAttributeValue("src", field2);
                    Control fieldRenderer = this.CreateFieldRenderer(args);
                    this.RenderControl(args, fieldRenderer, srcAttributeValue);
                }
                else
                {
                    base.RenderField(args);
                }
            }
        }
    }
}

Note the usage of this.StylelabsDataExtractor.GetAttributeValue("src", field2);

To get this code working instead of the original one we can patch the configs and insert this into the dependency injection:
<sitecore>
  <pipelines>
    <renderVariantField>
      <processor patch:instead="*[@type='Sitecore.XA.Feature.Stylelabs.Pipelines.RenderVariantField.RenderFallbackResponsiveMImage, Sitecore.XA.Feature.Stylelabs']"
				type="Foundation.Infrastructure.RenderFallbackResponsiveMImage, Foundation.Infrastructure" resolve="true" />
    </renderVariantField>
  </pipelines>
</sitecore>


That's it. Don't forget if you ever upgrade to remove this code of course. 


No comments:

Post a Comment