Sunday, March 9, 2025

 

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.

CODE 


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.

  1. Open Sitecore Content Editor
  2. Navigate to: /sitecore/system/Modules/Layout Service



      3. Create your Custom Folder inside   Rendering Contents Resolvers Folder



   
4. when folder is created need to create our custom resolver


5. create a resolver and set the your namspace and name





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