Quite often in Sitecore development you have to work with hierarchical item structures, which resemble your data model. Example could be a multi-level menu or, as in my case, forms. At the time of writing, out-of-the-box Sitecore JSS is not able to serialize item structures with arbitrary number of levels deep into a JSON tree. Luckily, like the most of Sitecore functionality, JSS is easily customizable š
The use case
To be more concrete, letās focus on the multi-level menu example, where
the amount of levels in the hierarchy is arbitrary. Letās say we have a datasourceĀ item structure like this:
Sitecore JSS comes with 5 out-of-the-box rendering contents resolvers. They are well described in here Customizing Layout Service Rendering Output , together with the information on how to implement a custom one. The Folder Filter ResolverĀ can serialize descendants of the datasource item, but it excludes folders and the result is just a āflatā array of items. For the above-mentioned example, the Layout Service output for the Menu rendering would be (some fields are omitted for simplicity):
{
"uid":"ff8e58f5-a7c7-484b-b9be-e1c2c63b0fb5",
"componentName":"Menu",
"dataSource":"{C3382D8D-3048-41D9-B3FC-80D037476BCD}",
"fields":{
"items":[
{
"name":"Home",
"fields":{ ... }
},
{
"name":"Categories",
"fields":{ ... }
},
{
"name":"Sitecore",
"fields":{ ... }
},
{
"name":"JSS",
"fields":{ ... }
},
{
"name":"DevOps",
"fields":{ ... }
]
}
}
As youĀ can see, menu items in the JSON output are flattened into an array, however, what I would like to achieve is (some fields are omitted for simplicity):
{
"uid":"ff8e58f5-a7c7-484b-b9be-e1c2c63b0fb5",
"componentName":"Menu",
"dataSource":"{C3382D8D-3048-41D9-B3FC-80D037476BCD}",
"fields":{
"items":[
{
"name":"Home",
"fields":{...}
},
{
"name":"Categories",
"fields":{
"items":[
{
"name":"Sitecore",
"fields":{
"items":[
{
"name":"JSS",
"fields":{...}
}
]
}
},
{
"name":"DevOps",
"fields":{...}
}
]
}
}
]
}
}
To accomplish this, I have implemented a custom Rendering Contents Resolver, which is able to serialize arbitrary hierarchies to a tree.
Implementing the resolver
Step 1: implement the code
Create a custom rendering contents resolver by inheriting from theĀ Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver
class.
Make sure to install theĀ Sitecore.LayoutService Nuget package to your project from Sitecoreās MyGet feed.
The code below recursively iterates through items, serializing their children to the āitemsā property. Note the use of the base classĀ ProcessItem
and ProcessItems
methods, which ensures the Experience Editor support.
Note:Ā since the below code recursively iterates through itemās .Children
Ā properly, this can lead to performance issues on large item trees (in my scenario the tree structures are rather small, so it is not an problem). Therefore, depending on your situation, you might want to optimize the code.Ā For example, the recursion can be replaced with a single call of the .GetDescendants()
method, which would result in better performance. Also make sure to use HTML caching of renderings on top of that.
Step 2: create a resolver Sitecore item
To be able to use the resolver in your renderings, you need to create a Rendering Contents Resolver item underĀ /sitecore/system/Modules/Layout Service/Rendering Contents Resolvers. For example:
Step 3: use the custom resolver in your rendering
Update the Rendering Contents ResolverĀ field in the Layout ServiceĀ section of your JSS Json Rendering.Ā
Alternative solutions
As an alternative to a custom Rendering Contents Resolver,Ā you could use the Sitecore GraphQL API to shape the JSON output for your rendering and include datasource item children. However, this would not work out of the box with arbitrary item hierarchies, since GraphQL does not support "recursive" queries, which is well described here: Dealing with arbitrary hierarchies .
Nevertheless, it is still a valid solution in case you have a fixed amount of levels of item children.Ā
For the menu items example with 3 levels of items, the GraphQL query (for the Integrated GraphQL ) might look like:
{
dsItem: item(path: $datasource) {
id
name
children {
... on MenuItem {
name
link {
value
}
children {
... on MenuItem {
name
link {
value
}
children {
... on MenuItem {
name
link {
value
}
}
}
}
}
}
}
}
}
Summary
There are multiple ways to deal with hierarchical item structures in JSS. As usual, the concrete approach depends on the use case.
- If item hierarchy is arbitrary, then you can go for a custom Rendering Contents Resolver. It will work for any rendering and data structure. This also means that the resolver can be reused in multiple renderings.
- In case amount of item levels in hierarchy is known in advance it might be enough to use GraphQL query to shape the output.
- However, if you use Connected Mode take into account that this will result into additional queries to Sitecoreās GraphQL endpoint.
- If you use Integrated Mode, there is no additional calls made and data is shaped within the Layout Service output**.**
- This approach is not reusable, meaning you will have to build specific queries for each component.