We will build on from the example provided by a Sitecore KB outlined here. The official Sitecore example adds fields to the SellableItem via a plugin i.e. the backend only. The full source code for the plugin is available download in the KB article. This example adds those fields to the front end using a SXA customization.
In this example we are going to do the following :-
- Add the standard Sitecore example “Notes” plugin to the commerce SDK and set the values for the new fields in the business tools.
- Ensure that the new fields are picked up in Sitecore (Data provider)
- Modify the standard “Product information” SXA component to display the “Warranty information” field on the product detail page.
Step 1 – Add the notes plugin sample to your SDK
- Open the Sitecore Commerce Engine SDK solution “Customer.Sample.Solution”
- Download and extract the notes sample plugin from the KB article above.
- Add the notes sample plugin to your SDK solution
- Add a reference to the notes plugin in the Sitecore.Commerce.Engine project. This tells the engine to use the new functionality. Your solution should look something like this…
- Build and start the engine
- Go to the business tools in your browser, usually https://localhost:4200
- Select a product in the business tools . If you are using the example catalog go to Merchandising > Catalogs > Habitat_Master > Categories > Departments > Appliances > “Habitat Viva 4-Door 22.0 Cubic Foot Refrigerator with Ice Maker”
- If the notes sample plugin loaded correctly, you will see another section for “Notes” and you will have an action that will open a new window where you can set the values for the new fields on your product.
- Add a value for “Warranty information”. Should look like this…
Step 2 – View the new fields in the Sitecore content editor
- Open the Sitecore content editor for your instance
- Go to the commerce tab on the Ribbon from anywhere in the content editor
- Select “Update data templates. This creates new sections in the templates for each component on the SellableItem in the commerce engine.
- To verify that the templates are updated go to/sitecore/templates/Commerce/Catalog Generated/ConnectSellableItem. Should look like this…
- Click the “Refresh the commerce cache” button
- Open your product from the commerce catalog in the content editor. Usually located somewhere like this “/sitecore/Commerce/Catalog Management/Catalogs/Habitat_Master/Habitat_Master-Departments/Habitat_Master-Appliances/6042567”
- You should see the value set for the “Warranty information” field.
Step 3 – Modify the SXA component to show the new field on the Site
Now we will create all the artifacts required to modify the SXA component. All we will do is extend the standard rendering model returned by the controller within a custom repository. We will also modify the existing view to include the new field.
Note: Later versions of SXA will include a mechanism for replacing the views (.cshtml) files like we do with the repositories and models below.
This will be the final result of the steps below:-
- Create a new solution in Visual studio
- Create a folder structure as follows “2. Feature\Feature.Catalog\Website”.
- Add a new website (or class library) project e.g. named “Sitecore.Services.Examples.Feature.Catalog.Website”
- Create a new folder structure in the project to store configuration changes, as follows “App_config\Include\z.Feature.Overrides”
- Add new patch config file to the new folder above named “zSitecore.Services.Examples.Foundation.Catalog.Website.Overrides.config”
- Add the following mark up to the file, it tells Sitecore to load our extended repository and model rather than the one provided with the OOB SXA component.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/"> | |
<sitecore> | |
<services> | |
<register serviceType="Sitecore.Commerce.XA.Feature.Catalog.Repositories.IProductInformationRepository, Sitecore.Commerce.XA.Feature.Catalog"> | |
<patch:attribute name="implementationType">Sitecore.Services.Examples.Feature.Catalog.Website.Repositories.ProductInformationRepository, Sitecore.Services.Examples.Feature.Catalog.Website</patch:attribute> | |
</register> | |
</services> | |
<commerce.XA> | |
<models> | |
<CatalogItemRenderingModel type="Sitecore.Commerce.XA.Feature.Catalog.Models.CatalogItemRenderingModel, Sitecore.Commerce.XA.Feature.Catalog"> | |
<patch:attribute name="type">Sitecore.Services.Examples.Feature.Catalog.Website.Models.CatalogItemRenderingModel, Sitecore.Services.Examples.Feature.Catalog.Website</patch:attribute> | |
</CatalogItemRenderingModel> | |
</models> | |
</commerce.XA> | |
</sitecore> | |
</configuration> |
- Add a new folder called “Models”
- Add a new class to the class to the folder called “CatalogItemRenderingModel”. We extend the original model with this class to include our new fields.
- Add the following code the class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Sitecore.Services.Examples.Feature.Catalog.Website.Models | |
{ | |
using Sitecore.Commerce.XA.Foundation.Common; | |
using Sitecore.Commerce.XA.Foundation.Common.Models; | |
using Sitecore.Commerce.XA.Foundation.Common.Providers; | |
public class CatalogItemRenderingModel : Sitecore.Commerce.XA.Feature.Catalog.Models.CatalogItemRenderingModel | |
{ | |
public CatalogItemRenderingModel(IStorefrontContext storefrontContext, IItemTypeProvider itemTypeProvider, IModelProvider modelProvider, IVariantDefinitionProvider variantDefinitionProvider) : | |
base(storefrontContext, itemTypeProvider, modelProvider, variantDefinitionProvider) | |
{ | |
} | |
public string WarrantyInformation { get; set; } | |
} | |
} | |
- Add a new folder called “Repositories”
- Add a new class to the class to the folder called “ProductInformationRepository”. We extend the original repository with this class to include logic to populate our new field.
- Add the following code the class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Sitecore.Commerce.XA.Feature.Catalog.MockData; | |
using Sitecore.Commerce.XA.Feature.Catalog.Models; | |
using Sitecore.Commerce.XA.Feature.Catalog.Repositories; | |
using Sitecore.Commerce.XA.Foundation.Catalog.Managers; | |
using Sitecore.Commerce.XA.Foundation.Common; | |
using Sitecore.Commerce.XA.Foundation.Common.Models; | |
using Sitecore.Commerce.XA.Foundation.Common.Search; | |
using Sitecore.Commerce.XA.Foundation.Connect; | |
using Sitecore.Commerce.XA.Foundation.Connect.Entities; | |
using Sitecore.Commerce.XA.Foundation.Connect.Managers; | |
using Sitecore.Data.Items; | |
using Sitecore.Diagnostics; | |
using Sitecore.Mvc.Presentation; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Web; | |
namespace Sitecore.Services.Examples.Feature.Catalog.Website.Repositories | |
{ | |
public class ProductInformationRepository : Sitecore.Commerce.XA.Feature.Catalog.Repositories.ProductInformationRepository | |
{ | |
public ProductInformationRepository(IModelProvider modelProvider, IStorefrontContext storefrontContext, ISiteContext siteContext, ISearchInformation searchInformation, ISearchManager searchManager, ICatalogManager catalogManager, ICatalogUrlManager catalogUrlManager) | |
: base(modelProvider, storefrontContext, siteContext, searchInformation, searchManager, catalogManager, catalogUrlManager) | |
{ | |
} | |
public override CatalogItemRenderingModel GetProductInformationRenderingModel(IVisitorContext visitorContext) | |
{ | |
var baseModel = this.GetProduct(visitorContext); | |
var extendedModel = baseModel as Models.CatalogItemRenderingModel; | |
Item currentCatalogItem = this.SiteContext.CurrentCatalogItem; | |
extendedModel.WarrantyInformation = currentCatalogItem["WarrantyInformation"]; | |
return extendedModel; | |
} | |
} | |
} |
- Add a new folder structure as follows called “Views\Commerce\Catalog”
- Browse to the view folder of your site and copy the “ProductInformation.cshtml” file into the folder created above. The path to the original file will look something like this “C:\inetpub\wwwroot\xp0.sc\Views\Commerce\Catalog\ProductInformation.cshtml”
- Edit the original file to add the new field, below is the full view
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@using Sitecore.XA.Foundation.MarkupDecorator.Extensions | |
@using Sitecore.XA.Foundation.SitecoreExtensions.Extensions | |
@using Sitecore.Commerce.XA.Feature.Catalog.Models | |
@using Sitecore.Commerce.XA.Foundation.Common.ExtensionMethods; | |
@model Sitecore.Services.Examples.Feature.Catalog.Website.Models.CatalogItemRenderingModel | |
@{ | |
if (Model == null) | |
{ | |
return; | |
} | |
} | |
<div @Html.Sxa().Component("cxa-productinformation-component", Model.Attributes)> | |
@if (!String.IsNullOrWhiteSpace(Model.ErrorMessage)) | |
{ | |
<div class="error-message"> | |
@Model.ErrorMessage | |
</div> | |
} | |
else | |
{ | |
<div class="product-info component-content"> | |
<div class="product-name"> | |
<h1 id="displayName">@Model.DisplayNameRender</h1> | |
</div> | |
<div class="product-number"> | |
<p> | |
@Html.Sitecore().Field("Item Number", Html.Sitecore().CurrentItem) | |
<span class="itemNumber">@Model.RenderField("ItemNumber")</span> | |
</p> | |
</div> | |
<div class="product-description"> | |
<h6>@Html.Sitecore().Field("Description", Html.Sitecore().CurrentItem)</h6> | |
<p class="description">@Model.DescriptionRender</p> | |
</div> | |
<div class="product-features" id="features"> | |
@Model.Features | |
</div> | |
<div class="product-warrantyinformation" id="warrantyinformation"> | |
@Model.WarrantyInformation | |
</div> | |
</div> | |
} | |
</div> |
- Your solution should look something like this…
- Build the solution
- Deploy the resultant assembly to your sites bin directory e.g. “C:\inetpub\wwwroot\xp0.sc\bin”
- Deploy the config file to your include directory e.g. “C:\inetpub\wwwroot\xp0.sc\App_Config\Include\z.Feature.Overrides” (matches the structure of the project)
- Deploy the view (cshtml) file to your commerce view directory (where you copied it from) e.g. “C:\inetpub\wwwroot\xp0.sc\Views\Commerce\Catalog” (matches the structure of the project)
Note : Automating the deployment using gulp or another mechanism would be better but doing it manually is not a bad way to understand where things should go.
- Verify that your custom repos and models have been loaded by browsing your site config from for example “https://sxa.storefront.com/sitecore/admin/showconfig.aspx”, you should be able to search the config and find your custom name space containing your new models etc.
- You can attach to the w3wp.exe process using visual studio to debug the repo.
In the second step, when I click the “Update data templates” button, the exception is thrown.
An error occured
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.ArgumentNullException: Value cannot be null. Parameter name: key at System.Collections.Concurrent.ConcurrentDictionary`2.ContainsKey(TKey key) at Sitecore.Commerce.Engine.Connect.DataProvider.CatalogRepository.GetEntityView(String sitecoreId, String viewName, String forAction, String itemId) at Sitecore.Commerce.Engine.Connect.DataProvider.Templates.CatalogTemplateGenerator.BuildCatalogTemplates(Database database) — End of inner exception stack trace — at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at Sitecore.Reflection.ReflectionUtil.InvokeMethod(MethodInfo method, Object[] parameters, Object obj) at Sitecore.Jobs.JobRunner.RunMethod(JobArgs args) at (Object , Object[] ) at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain) at Sitecore.Jobs.Job.DoExecute() at Sitecore.Jobs.Job.ThreadEntry(Object state)
I wonder if I can consult you by email. My email address is f18995864647@163.com.
Looking forward to receiving your email.
LikeLike
Hi Yingfeng, I have seen this error when the site cannot communicate with commerce engine correctly. Are you able to see your catalogs in the Sitecore content editor?
LikeLike
Hi websterian,I have solved the above problem because of the verification problem.
But I found some problems following your operation.
currentCatalogItem[“WarrantyInformation”] The result is “”, I don’t see the correct value.
I wanted to give you a screenshot, but I can’t put a picture here.There is no problem with the other configuration, if we can, can we talk about it by email?
LikeLike
Hey Yingfeng, can you contact your Sitecore rep and ask them to forward me the email? Apologies, I am not allowed to directly contact partners or clients from here.
LikeLike
Hi, i completed step 1. But new section “Notes” doesnt seem.
LikeLike
Hello Websterian, If i want to store the list of Notes in the Notes View , then where is the code change in the component?
LikeLike
Hey man, apologies for the late reply, don’t check comments very often. The code to update the notes component is in the edit view code i.e. DoActionEditNotesBlock. That what you looking for?
LikeLike
Does anyone know how to deal with list-properties? I have a ‘public List’. Where the LocalizedSpecificationItem on itself has properties. It does not generate any templates for me, guess it’s not supported.
LikeLike
If I want to store a list of notes in the notes view, how do I do it? Request that guide me. If there is an example, please give me a link.
LikeLike