Sitecore Headless – Wildcard item with datasource content resolver

On one of my first Sitecore Headless project I got a requirement to implement a dynamic URL resolving to display product data from a different root of the Sitecore tree. If it would have been a traditional Sitecore solution I could implement a custom item resolver but in the headless world we need a different kind of mindset to solve problems like this.

From my experience, to make the development easy better to implement almost everything in component level and not on a global level to make the solution flexible. This answer was a good starting point for me. But I wanted to merge together the dynamic item and the datasource item and serve it together for the client application.

Why?

In my case the datasource item is storing some static content which should be the same for all pages (e.g. labels), but in the same component I want to reach the dynamic data which is resolved and should be different on each page. To make this work, the key method which is merging the two datasources into one JSON object is the JContainer.Merge.

public class ProductEntityContentsResolver : RenderingContentsResolver
{
private readonly IProductService _productService;
public ProductEntityContentsResolver(IProductService productService)
{
_productService = productService;
}
protected override Item GetContextItem(Rendering rendering, IRenderingConfiguration renderingConfig)
{
if (!Context.PageMode.IsNormal)
{
// Always return with a default Product item if the page is in edit/preview mode
return _productService.GetFallbackProductEntity();
}
if (Context.Item == null
|| HttpContext.Current?.Request?.Path == null
|| Context.Item.TemplateID.ToString() != Templates.ProductDetailsPage.Id)
{
return Context.Item;
}
var productId = GetProductId(HttpContext.Current.Request.QueryString["item"]);
if (string.IsNullOrEmpty(productId))
{
return Context.Item;
}
var productEntity = _productService.SearchForProductEntity(productId);
if (productEntity == null)
{
return Context.Item;
}
return productEntity;
}
protected override JObject ProcessItem(Item item, Rendering rendering, IRenderingConfiguration renderingConfig)
{
var contextItemJson = base.ProcessItem(item, rendering, renderingConfig);
// Return only dynamic content if no datasource is set
if (string.IsNullOrEmpty(rendering?.DataSource)
|| !ID.TryParse(rendering.DataSource, out ID datasourceId))
return contextItemJson;
var datasourceItem = Context.Database.GetItem(datasourceId);
if (datasourceItem == null) return contextItemJson;
var datasourceItemJson = base.ProcessItem(datasourceItem, rendering, renderingConfig);
if (datasourceItemJson == null) return contextItemJson;
if (contextItemJson != null)
{
contextItemJson.Merge(
datasourceItemJson,
new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Union });
return contextItemJson;
}
else
{
// If contextItem is empty, fallback to the datasourceItem
return datasourceItemJson;
}
}
private string GetProductId(string path)
{
var id = path?.Split('/').Last();
return id ?? string.Empty;
}
}

The most important part is in line 57 which merges together the datasource item and the resolved context item. This JSON merge will end up with an object like this:

{
"componentName": "ProductDetails",
"dataSource": "{725E7C7C-FDCE-4577-B9E8-B2C1345AE24C}",
"fields": {
// resolved dynamic item field
"ProductName": {
"value": "#SitecoreLunch sticker 🍩"
},
// resolved dynamic item field
"Price": {
"value": 10.00
},
// static datasource field
"BackButtonLabel": {
"value": "Back to products"
}
}
}

Happy merging! 🧬

2 thoughts on “Sitecore Headless – Wildcard item with datasource content resolver

  1. Great article! Since you’re fetching data from a product service in a RenderingContentsResolver it could slow down your entire application if the product service is slow. Because the headless rendering will first run all components before showing the JSON of a page. You could solve that by using the Hybrid Placeholder. It can fetch the products in a RenderingContentsResolver async while the page is already loaded: https://github.com/jbreuer/Hybrid-Placeholder

    Like

    1. Nice and good point! 👏 Although in this example the _productService is getting data from Sitecore, just from another root of the content tree.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s