5 min to read
Sitecore JSS: Extending the Layout Service
JSS (JavaScript Services) is Sitecore's answer to the need of having a decoupled CMS. One of the reasons for the popularity of the headless architecture is because you can redesign your CMS without setting up a new implementation of the CMS itself. A second reason is that developers do not need to know anything about the backend system. JSS is highly flexible and out of the box it supports a a lot when it comes to customization. Nevertheless, we sometimes need to extend the reality as we see it ;-)
Customization areas
JSS is highly flexible and has a lot of options to get your data from the service endpoint. And if you want to do even more you can get it through GraphQL out of the box by configuring a query on the rendering. And if that isn't enough you can extend the layout service. Extending can be done in the following areas:
- Layout Service context - You can add/change data about the page or site.
- Rendering contents - Change the way the component data is presented. These are the field values.
- Field Serializers - Change the way the field is outputted.
In this post I will show you how easy it is to change the rendering contents.
Customizing the Layout Service Rendering Output
There are three ways of changing the output of the service to your needs. Two ways can be done by configuration and is done with ease through the content editor:
Configuring the built in Rendering GraphQL support - If you are new to GraphQL make sure to check out my post Sitecore JSS: Create your own GraphQL schema. GraphQL is the new way of querying an API with a JSON-payload. Some like REST, but much more flexible and maintainable. In regard to configuring the queries two tastes can be chosen here: connected mode
or integrated mode
. With the connected mode
data is passed through the GraphQL query from the client code. The layout service will not handle this request and you surely have to take care that you've selected. Using the integrated mode
the data is returned through the layout service.
Configuring a builtin Rendering Contents Resolver - The content resolvers are a builtin mechanism which allows you to configure a rendering in that way that the data returned matches the configuration. When you install JSS, you will get five resolvers out of the box:
Datasource Resolver
- This is the default and serializes your datasource.Datasource Item Children Resolver
- Serializes the children of the datasource but not itself.Context Item Resolver
- Serializes the context item.Context Item Children Resolver
- Serializes the children of the context item.Folder Filter Resolver
- Serializes the descendents of a datasource, but not folders (because of the Item Selector Query:.//*[@@templateid!='{A87A00B1-E6DB-45AB-8B54-636FEC3B5523}']
)
The Item Selector Query is a very powerful mechanism but use it with care because it can influence the performance
Create a RenderingContentsResolver - And because I love programming this is my favorite !! If any of the out of the box solutions does not fit your needs, you must code it yourself. And this time I want to change the way the data is serialized and returned from the service. Fist, we are going to create a C# resolver class. There are two options to do so. You can choose to implement the IRenderingContentsResolver
interface and build the implement the logic yourself or inherit from the RenderingContentsResolver
class. The second one is great as it has some methods to override which are usefull when you want to resolve items from elsewhere in the content tree.
Then we have to create the Content Resolver item and bind it to the class and the assembly name. We leave use context item
unchecked because we want to use our datasource and even extend the template.
Now we need to create the custom code. No extra configuration in Sitecore is needed to be able to use this code. So its sufficient when the assembly resides in the bin-folder.
And then the code:
using Newtonsoft.Json.Linq;
using Sitecore.Data.Items;
using Sitecore.LayoutService.Configuration;
using Sitecore.LayoutService.ItemRendering.ContentsResolvers;
using Sitecore.Mvc.Presentation;
using System.Collections.Generic;
using Foundation.Macaw.Dictionary.Helpers;
using Foundation.Macaw.Items.Interfaces;
using Foundation.Macaw.Modeling.Helpers;
using Foundation.Macaw.Modeling.Interfaces;
using Foundation.Macaw.Modeling.Models.ViewModels;
using System.Web.Mvc;
namespace Macaw.Sitecore.LayoutService.ItemRendering.ContentsResolvers
{
public class CustomRenderingContentsResolver : RenderingContentsResolver
{
protected readonly IItemService _itemService;
protected readonly IFeatureModelService _featureModelService;
public CustomRenderingContentsResolver()
{
_itemService = DependencyResolver.Current.GetService();
_featureModelService = DependencyResolver.Current.GetService();
}
public override object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
{
var datasource = !string.IsNullOrEmpty(rendering.DataSource)
? rendering.RenderingItem?.Database.GetItem(rendering.DataSource)
: null;
var boilerplate = new {
datasource.ID,
datasource.Name,
Datasource = GetItemViewModel(),
Parameters = RenderingParametersModelHelper.GetRenderingParameters(),
Dictionary = DictionaryItemsHelper.GetRenderingDictionaryItems(),
};
object json = base.ResolveContents(rendering, renderingConfig);
return new { fromContentResolver = json, macaw = boilerplate };
}
private ItemViewModel GetItemViewModel()
{
var item = _itemService.GetLayoutItem();
return ViewModelHelper.GetItemViewModel(item);
}
}
}
</code>
</pre>Just drop the dll in the bin folder and make sure the binding in the resolver item in the content editor matches. Then let's move to the rendering and select our created item:
Secondly, we want to change our template to give it some more data:
Now let's fill in the fields of the item we had:
And finally, let's request our data from the API:
From the code you can see that I output an anonymous type holding two properties. The first one is the resolved data by JSS (fromContentResolver) and the second part is the contents that are resolved by the custom logic (macaw). There are two things that stand out right away when you look at the field structure returned from the layout service. The data in the fromContentResolver part hold all field values in a list having its name and a value property which exposes the value. In addition, it also got the data from the rest of the resolved items from my custom logic. The custom logic, however, holds business logic like inheritance of field values. This means that when the SEO title is blank it will take the title of the parent.
Comments