Part 5 describes how to create a dynamic product page. This is great for navigating the catalog, but does not allow a content editor to select products to display on (e.g.) a landing page. This part presents how to create a product cluster component that enables displaying a configured set of products.
JSS Commerce parts:
Let's first create a ProductCluster
React JSS component;
import React from 'react';
import ProductList from '../../shared/components/productlist';
// ProductCluster displays products from the resolved datasource
const ProductCluster = ({fields}) => (
<div className='productcluster'>
<h1 className='productcluster__title'>{fields.heading}</h1>
{<ProductList products={fields.products}/>}
</div>
);
export default ProductCluster;
The component takes a heading text and products as field
properties, and it re-uses the ProductList
components that was introduced in part 5.
To enable a content editor to select the products to display we will use the Sitecore Query Builder. In this way one can for example create a query for displaying all action products:
The query can than be used on a page component, e.g. for listing action products on the home page:
Now we only need to create a list of products out of the query. For this we create a content resolver: ProductsContentResolver
.
The ProductsContentResolver
takes query selection field from the context item and performs a LinQ contentsearch query.
That's all, the complete code for the ProductsContentResolver
is:
public class ProductsContentResolver : RenderingContentsResolver
{
public override object ResolveContents(Sitecore.Mvc.Presentation.Rendering rendering, IRenderingConfiguration renderingConfig)
{
var contextItem = GetContextItem(rendering, renderingConfig);
if (contextItem == null)
{
return new { };
}
var selectionField = (DatasourceField)contextItem.Fields[Templates.Products.Selection];
var query = selectionField.Value;
if (String.IsNullOrEmpty(query))
{
return new { };
}
var searchItems = new List<CommerceSellableItemSearchResultItem>();
var searchManager = CommerceTypeLoader.CreateInstance<ICommerceSearchManager>();
using (var context = searchManager.GetIndex().CreateSearchContext())
{
var searchStringModels = SearchStringModel.ParseDatasourceString(query);
searchItems = LinqHelper.CreateQuery<CommerceSellableItemSearchResultItem>(context, searchStringModels)
.Where(x => x.CommerceSearchItemType == CommerceSearchItemType.SellableItem)
.Where(x => x.Language == Sitecore.Context.Language.Name)
.ToList();
}
// Distinct products only as the products might exist at multiple places in the catalog
searchItems = searchItems
.GroupBy(x => x.ProductId)
.Select(x => x.First())
.ToList();
// Create products from search result
var products = new List<object>();
foreach (var searchItem in searchItems)
{
var sitecoreItem = searchItem.GetItem();
// Add variants
if (sitecoreItem.HasChildren)
{
foreach (Sitecore.Data.Items.Item child in sitecoreItem.GetChildren())
{
products.Add(GetProduct(searchItem, sitecoreItem, child.Name));
}
}
// No variants add single product
else
{
products.Add(GetProduct(searchItem, sitecoreItem));
}
}
return new
{
Heading = ((TextField)contextItem.Fields[Templates.Products.Heading]).Value,
Products = products.ToArray()
};
}
private object GetProduct(CommerceSellableItemSearchResultItem searchItem, Sitecore.Data.Items.Item sitecoreItem, string variantId = null)
{
MultilistField field = sitecoreItem.Fields["Images"];
var imageId = field.Items?.First();
return new
{
searchItem.Path,
searchItem.ProductId,
searchItem.DisplayName,
Description = sitecoreItem["Description"],
ImageId = imageId,
VariantId = variantId
};
}
}
After configuring the ProductsContentResolver
as content resolver for the ProductCluster
component and placing the ProductCluster
on the home page, the product selection is show on the home page:
Conclusion: the reusability of React components in combination with the configurability of JSS really shines for this use-case!
© Joost Meijles 2019