1

EDIT 1

I was able to get this functioning by overriding the GetModel() method after recieving some guidance from Gatogordo and Dawid Rutkowski and decompling the sitecore DLLs. See below for answer including working code.

Original Question

I'm working with the new SXA 1.4 module that Sitecore recently released. After a lot of trial and error I was able to get a basic custom control working and added to the SXA toolbox. I figured the logical next task was to update my control to use different Rendering Variants but I've run into some problems. I started by modifying based on these blog posts:

Here are my updated code files:

CallToActionWithImageBackground.config

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <ioc>
                <processor type="Company.Feature.CallToActionWithImageBackground.Configurator.CallToActionWithImageBackgroundConfigurator, Company.Feature.CallToActionWithImageBackground" />
            </ioc>
        </pipelines>
    </sitecore>
</configuration>

CallToActionWithImageBackgroundConfigurator.cs

using Sitecore.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Company.Feature.CallToActionWithImageBackground.Repositories;
using Company.Feature.CallToActionWithImageBackground.Controllers;
using Sitecore.XA.Foundation.IOC.Pipelines.IOC;

namespace Company.Feature.CallToActionWithImageBackground.Configurator
{
    public class CallToActionWithImageBackgroundConfigurator : IocProcessor
    {
        public override void Process(IocArgs args)
        {
            args.ServiceCollection.AddTransient<ICallToActionWithImageBackgroundRepository, CallToActionWithImageBackgroundRepository>();
        }
    }
}

CallToActionWithImageBackgroundController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Sitecore;
using Sitecore.Data;
using Sitecore.ExperienceEditor;
using Company.Feature.CallToActionWithImageBackground.Repositories;
using Sitecore.XA.Foundation.IoC;
using Sitecore.XA.Foundation.Mvc.Controllers;
using Sitecore.XA.Foundation.RenderingVariants.Controllers;
using Company.Feature.CallToActionWithImageBackground.Models;

namespace Company.Feature.CallToActionWithImageBackground.Controllers
{
    public class CallToActionWithImageBackgroundController : VariantsController
    {

        private readonly ICallToActionWithImageBackgroundRepository _repository;

        public CallToActionWithImageBackgroundController(ICallToActionWithImageBackgroundRepository repository)
        {
            this._repository = repository;
        }

        protected object GetVariantsModel()
        {
            return _repository.GetModel();
        }

        //public ActionResult BlogList()
        //{
        //    var model = _repository.GetModel();
        //    return View(model);
        //}

    }
}

CallToActionWithImageBackgroundModel.cs

using Sitecore.XA.Foundation.Multisite.LinkManagers;
using Sitecore.XA.Foundation.Mvc.Models;
using Sitecore.XA.Foundation.RenderingVariants.Models;
using Sitecore.XA.Foundation.Variants.Abstractions.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Company.Feature.CallToActionWithImageBackground.Models
{
    public class CallToActionWithImageBackgroundModel : VariantsRenderingModel
    {

    }
}

CallToActionWithImageBackgroundRepository.cs

using Company.Feature.CallToActionWithImageBackground.Models;
using Sitecore.XA.Foundation.Multisite.LinkManagers;
using Sitecore.XA.Foundation.Mvc.Repositories.Base;
using Sitecore.XA.Foundation.RenderingVariants.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
//using static Sitecore.Configuration.Settings;

namespace Company.Feature.CallToActionWithImageBackground.Repositories
{
    public class CallToActionWithImageBackgroundRepository : VariantsRepository, ICallToActionWithImageBackgroundRepository
    {

        //public T GetModel<T>(object model) where T : IRenderingModelBase
        //{
        //    FillBaseProperties(model);
        //    return (T)model;
        //}

        //public CallToActionWithImageBackgroundModel GetModel<CallToActionWithImageBackgroundModel>(object model)
        //{
        //    FillBaseProperties(model);
        //    return (CallToActionWithImageBackgroundModel)model;
        //}

        public override IRenderingModelBase GetModel()
        {
            CallToActionWithImageBackgroundModel model = new CallToActionWithImageBackgroundModel();
            FillBaseProperties(model);
            return model;
        }

    }
}

ICallToActionWithImageBackgroundRepository.cs

using Sitecore.XA.Foundation.Mvc.Repositories.Base;
using Sitecore.XA.Foundation.RenderingVariants.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Company.Feature.CallToActionWithImageBackground.Repositories
{
    public interface ICallToActionWithImageBackgroundRepository : IVariantsRepository
    {

    }
}

Call to Action With Image Background.cshtml

@using Sitecore.XA.Foundation.MarkupDecorator.Extensions
@using Sitecore.XA.Foundation.SitecoreExtensions.Extensions
@using Sitecore.Data;
@using Sitecore.Extensions
@using Sitecore.XA.Foundation.MarkupDecorator.Extensions
@using Sitecore.XA.Foundation.SitecoreExtensions.Extensions
@using Sitecore.XA.Foundation.RenderingVariants.Extensions
@using Sitecore.XA.Foundation.RenderingVariants.Fields
@using Sitecore.XA.Foundation.Variants.Abstractions.Fields

@model Company.Feature.CallToActionWithImageBackground.Models.CallToActionWithImageBackgroundModel

@if (Model.DataSourceItem != null || Html.Sxa().IsEdit)
{
    <div @Html.Sxa().Component("call-to-action-with-image-background-component", Model.Attributes)>
        <div class="component-content">
            @if (Model.DataSourceItem == null)
            {
                @Model.MessageIsEmpty
            }
            else
            {
                foreach (BaseVariantField variantField in Model.VariantFields)
                {
                    @Html.RenderingVariants().RenderVariant(variantField, Model.Item, Model.RenderingWebEditingParams)
                }
            }
        </div>
    </div>
}

And here are my settings within Sitecore:

enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

And the error message I receive:

11708 15:59: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)
   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()
   at Sitecore.Mvc.Presentation.ControllerRenderer.Render(TextWriter writer)
   at Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args)

Nested Exception

Exception: System.InvalidOperationException
Message: The model item passed into the dictionary is of type 'Sitecore.XA.Foundation.RenderingVariants.Models.VariantListsRenderingModel', but this dictionary requires a model item of type     'Company.Feature.CallToActionWithImageBackground.Models.CallToActionWithImageBackgroundModel'.
Source: System.Web.Mvc
   at System.Web.Mvc.ViewDataDictionary`1.SetModel(Object value)
   at System.Web.Mvc.ViewDataDictionary..ctor(ViewDataDictionary dictionary)
   at System.Web.Mvc.WebViewPage`1.SetViewData(ViewDataDictionary viewData)
   at System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)
   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)


8140 16:00:05 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)
   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()
   at Sitecore.Mvc.Presentation.ControllerRenderer.Render(TextWriter writer)
   at Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args)

Nested Exception

Exception: System.InvalidOperationException
Message: The model item passed into the dictionary is of type 'Sitecore.XA.Foundation.RenderingVariants.Models.VariantListsRenderingModel', but this dictionary requires a model item of type     'Company.Feature.CallToActionWithImageBackground.Models.CallToActionWithImageBackgroundModel'.
Source: System.Web.Mvc
   at System.Web.Mvc.ViewDataDictionary`1.SetModel(Object value)
   at System.Web.Mvc.ViewDataDictionary..ctor(ViewDataDictionary dictionary)
   at System.Web.Mvc.WebViewPage`1.SetViewData(ViewDataDictionary viewData)
   at System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)
   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)

I feel like I am doing something silly but I have never worked with Sitecore before so the learning curve has been rather drastic. Any ideas what I could be doing wrong?

3
  • In your controller class, can you put the code for the index method? As the error you are getting is because the model passed to 'Call to Action With Image Background' view is not the same as defined in the cshtml.
    – adarsh
    Commented Aug 7, 2017 at 20:17
  • You probably are using GetModel() method instead of GetVariantsModel(). Override Index() which is using GetModel() by default. Commented Aug 7, 2017 at 20:22
  • Thank you for your help @DawidRutkowski! I've posted an answer with what I ended up doing to get this working which was a little different than what you suggested. My reasoning for not overriding Index was that I thought I may want to add custom ActionResults in the future and if I did I would want them to automatically get the correct model. I'm very new to Sitecore so I do not know if my solution is a good idea or if I'm doing some bad things, so I'd love to get your opinion on what I came up with.
    – Nathan
    Commented Aug 25, 2017 at 15:52

2 Answers 2

1

Dawid is right in his comment: the problem is that you are using the built-in Index method in the controller. This index method uses the GetModel method - which will go to the default ModelRepository and not your CallToActionWithImageBackgroundRepository.

You have a few options to fix this:

  • override Index and make it use your repository to get the model
  • create a new action in the controller and change the config in Sitecore to refer to the action

Both cases come down to the same actually: write your own action in the controller to get the model from your custom repository.

1
  • Thank you for your help @Gatogordo! I've posted an answer with what I ended up doing to get this working which was a little different than what you suggested. My reasoning for not overriding Index was that I thought I may want to add custom ActionResults in the future and if I did I would want them to automatically get the correct model. I'm very new to Sitecore so I do not know if my solution is a good idea or if I'm doing some bad things, so I'd love to get your opinion on what I came up with.
    – Nathan
    Commented Aug 25, 2017 at 15:55
1

After decompiling the Sitecore DLLs I found that I had to override the GetModel() function to ensure the view was receiving the custom model. I ended up removing the CallToActionWithImageBackground project from my original question so here is another example project named IconListItem that is working in my environment.

IconListItem.config

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <ioc>
                <processor type="Company.Feature.IconListItem.Configurator.IconListItemConfigurator, Company.Feature.IconListItem" />
            </ioc>
        </pipelines>
    </sitecore>
</configuration>

IconListItemConfigurator.cs

using Company.Feature.IconListItem.Repositories;
using Microsoft.Extensions.DependencyInjection;
using Sitecore.XA.Foundation.IOC.Pipelines.IOC;

namespace Company.Feature.IconListItem.Configurator
{
    public class IconListItemConfigurator : IocProcessor
    {
        public override void Process(IocArgs args)
        {
            args.ServiceCollection.AddTransient<IIconListItemRepository, IconListItemRepository>();
        }
    }
}

IconListItemController.cs

using Company.Feature.IconListItem.Models;
using Company.Feature.IconListItem.Repositories;
using Sitecore.XA.Foundation.RenderingVariants.Controllers;

namespace Company.Feature.IconListItem.Controllers
{
    public class IconListItemController : VariantsController
    {

        private readonly IIconListItemRepository _repository;

        public IconListItemController(IIconListItemRepository repository)
        {
            this._repository = repository;
        }

        protected override object GetModel()
        {
            var model = _repository.GetModel<IconListItemModel>(new IconListItemModel());
            return model;
        }

    }
}

IconListItemModel.cs

using Sitecore.XA.Foundation.Variants.Abstractions.Models;

namespace Company.Feature.IconListItem.Models
{
    public class IconListItemModel : VariantsRenderingModel
    {
    }
}

IconListItemRepository.cs

using Sitecore.XA.Foundation.Mvc.Repositories.Base;
using Sitecore.XA.Foundation.RenderingVariants.Repositories;

namespace Company.Feature.IconListItem.Repositories
{
    public class IconListItemRepository : VariantsRepository, IIconListItemRepository
    {

        public T GetModel<T>(object model) where T : IRenderingModelBase
        {
            FillBaseProperties(model);
            return (T)model;
        }

    }
}

IIconListItemRepository.cs

using Sitecore.XA.Foundation.Mvc.Repositories.Base;
using Sitecore.XA.Foundation.RenderingVariants.Repositories;

namespace Company.Feature.IconListItem.Repositories
{
    public interface IIconListItemRepository : IVariantsRepository
    {
        T GetModel<T>(object model) where T : IRenderingModelBase;
    }
}

Icon List Item.cshtml

@using Sitecore.XA.Foundation.MarkupDecorator.Extensions
@using Sitecore.XA.Foundation.SitecoreExtensions.Extensions
@using Sitecore.XA.Foundation.MarkupDecorator.Extensions
@using Sitecore.XA.Foundation.SitecoreExtensions.Extensions

@model Company.Feature.IconListItem.Models.IconListItemModel

@if (Model.DataSourceItem != null || Html.Sxa().IsEdit)
{
    <div @Html.Sxa().Component("company-icon-list-item-component", Model.Attributes)>
        <div class="component-content">
            <div class="company-icon-list-item-image">@Html.Sxa().Field(Company.Feature.IconListItem.Templates.IconListItem.Content.Fields.Image)</div>
            <h5 class="company-icon-list-item-title">@Html.Sxa().Field(Company.Feature.IconListItem.Templates.IconListItem.Content.Fields.Title)</h5>
            <p class="company-icon-list-item-content">@Html.Sxa().Field(Company.Feature.IconListItem.Templates.IconListItem.Content.Fields.Content)</p>
            <div class="company-icon-list-item-link">
                @Html.Sxa().Field(Company.Feature.IconListItem.Templates.IconListItem.Content.Fields.Link)
            </div>
        </div>
    </div>
}

Templates.cs

namespace Company.Feature.IconListItem
{
    using Sitecore.Data;

    public struct Templates
    {
        public struct IconListItem
        {
            public static readonly ID ID = new ID("{FAKEGUID-0000-0000-0000-000000000000}");

            public struct Content
            {
                public static readonly ID ID = new ID("{FAKEGUID-0000-0000-0000-000000000000}");

                public struct Fields
                {
                    public static readonly ID Image = new ID("{FAKEGUID-0000-0000-0000-000000000000}");

                    public static readonly ID Title = new ID("{FAKEGUID-0000-0000-0000-000000000000}");
                    public const string Title_FieldName = "Title";

                    public static readonly ID Content = new ID("{FAKEGUID-0000-0000-0000-000000000000}");
                    public const string Content_FieldName = "Content";

                    public static readonly ID Link = new ID("{FAKEGUID-0000-0000-0000-000000000000}");
                    public const string Link_FieldName = "Link";

                }
            }

        }
    }
}

enter image description here enter image description here enter image description here enter image description here enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.