Creating a Custom Resolver for Descendants with
Pagination in Sitecore JSS
When working with Sitecore’s Layout Service, sometimes we need to retrieve items with their children in a paginated format. In this blog, we will walk through the creation of a custom resolver in Sitecore that retrieves a given item and its child items with pagination support.
Why Do We Need a Custom Resolver?
By default, Sitecore provides content resolvers, but they
may not always fit our needs, especially when working with large datasets where
performance optimization is essential. A custom resolver helps
control the data we return, structure the JSON output, and apply pagination.
Steps to Create a Custom Resolver
1. Setup the Class Structure
To create a custom resolver, we need to extend the RenderingContentsResolver
class. This allows us to control how Sitecore serializes content for the Layout
Service.
Below is the PaginatedChildrenResolver class, which
does the following:
- Retrieves
the context item or the specified data source item.
- Applies
pagination to fetch child items.
- Adds
metadata (e.g., total count, URLs, and updated dates).
- Supports
droplink field resolution to fetch related item children.
using Sitecore.Abstractions;
using Sitecore.Data.Fields;
using Sitecore.LayoutService.Configuration;
using System.Collections.Generic;
using System.Web;
using System;
using Sitecore.Data.Items;
using System.Linq;
using Newtonsoft.Json.Linq;
using Sitecore.Mvc.Presentation;
using
Sitecore.LayoutService.ItemRendering.ContentsResolvers;
namespace Resolvers.Foundation.CustomResolvers
{
public class PaginatedChildrenResolver:
RenderingContentsResolver
{
private readonly
BaseLinkManager _linkManager;
private string
_defaultPageSize = "10";
public PaginatedChildrenResolver
(BaseLinkManager linkManager)
{
_linkManager = linkManager;
}
public override
object ResolveContents(Rendering rendering, IRenderingConfiguration
renderingConfig)
{
var result
= new JObject
{
[CustomResolversConstant.Children]
= new JArray(),
[CustomResolversConstant.TotalCount]
= 0
};
_defaultPageSize = GetRenderingParameters(rendering);
var
(pageSize, currentPage) = GetPaginationParameters();
var
contextItem = GetDataSourceItem(rendering);
if
(contextItem == null)
{
return
new JObject();
}
result =
ProcessItem(contextItem, rendering, renderingConfig);
var offset
= (currentPage - 1) * pageSize;
var items
= GetPaginatedItems(contextItem, offset, pageSize);
result[CustomResolversConstant.Children]
= ProcessChildren(contextItem, items, rendering, renderingConfig);
// Resolve
Droplink fields
contextItem.Fields.ReadAll();
foreach
(Field field in contextItem.Fields)
{
if
(result[field.Name] != null)
{
var
droplinkTarget = ItemExtensions.GetDroplinkTarget(contextItem, field.Name);
if
(droplinkTarget != null && droplinkTarget.HasChildren)
{
result[field.Name][CustomResolversConstant.Children] = ProcessChildren(
droplinkTarget, droplinkTarget.Children?.ToList(), rendering,
renderingConfig);
}
}
}
result[CustomResolversConstant.TotalCount] =
GetTotalItemCount(contextItem);
return
result;
}
private JArray
ProcessChildren(Item parentItem, List<Item> children, Rendering
rendering, IRenderingConfiguration renderingConfig)
{
var result
= new JArray();
if
(children != null && children.Count > 0)
{
foreach
(var child in children.Where(x => x.ParentID == parentItem.ID))
{
JObject itemJson = ProcessItem(child, rendering, renderingConfig);
var
resultJson = new JObject
{
[CustomResolversConstant.Id] = child.ID.Guid.ToString(),
[CustomResolversConstant.TemplateId] = child.TemplateID.Guid.ToString(),
[CustomResolversConstant.Name] = child.Name,
[CustomResolversConstant.DisplayName] = child.DisplayName,
[CustomResolversConstant.UpdatedDate] = child.Statistics.Updated,
[CustomResolversConstant.Fields] = itemJson,
[CustomResolversConstant.Url] = child.HasPresentationDetails() ?
_linkManager.GetItemUrl(child) : null,
};
result.Add(resultJson);
}
}
return
result;
}
private Item GetDataSourceItem(Rendering
rendering)
{
return !string.IsNullOrEmpty(rendering.DataSource)
?
Sitecore.Context.Database.GetItem(rendering.DataSource)
:
Sitecore.Context.Item;
}
private
List<Item> GetPaginatedItems(Item parentItem, int offset, int pageSize)
{
return
pageSize > 0
?
parentItem.GetChildren().Skip(offset).Take(pageSize).ToList()
:
parentItem.GetChildren().ToList();
}
private int GetTotalItemCount(Item
parentItem)
{
return
parentItem.GetChildren().Count;
}
private (int
pageSize, int currentPage) GetPaginationParameters()
{
int
pageSize = int.TryParse(_defaultPageSize, out int parsedSize) ? parsedSize : 10;
int
currentPage = 1;
var
request = HttpContext.Current?.Request;
if
(request != null && !string.IsNullOrEmpty(request.QueryString[CustomResolversConstant.Page]))
{
int.TryParse(request.QueryString[CustomResolversConstant.Page],
out currentPage);
}
return
(pageSize, currentPage <= 0 ? 1 : currentPage);
}
private string
GetRenderingParameters(Rendering rendering)
{
return
rendering.Parameters[CustomResolversConstant.PageCount]?.ToString() ?? "10";
}
}
}
2. Register the Resolver in Sitecore
Once our resolver is created, we need to register it
in Sitecore’s rendering contents resolver configuration.
- Open Sitecore
Content Editor
- Navigate to: /sitecore/system/Modules/Layout Service
Final Thoughts
With the PaginatedChildrenResolver, we now have an optimized way to fetch child items with pagination in Sitecore Layout Service. This improves performance, reduces API response size, and provides a structured response.
Next Steps
- Extend this resolver to include sorting
- Implement custom filtering options
- Add caching mechanisms for improved efficiency
No comments:
Post a Comment