Walkthrough: Creating a model-bound view that renders a contents resolver

Version: 10.1

You use contents resolvers with the Sitecore Layout Service to provide more complex data beyond the serialization of a component data source. Common use cases include site navigation, search results, or returning the output or state of custom business logic. In this walkthrough, you learn how to create a contents resolver that returns a model of the content tree for you to render in a model-bound view.

When you have completed the walkthrough, we recommend you familiarize yourself with the Sitecore Helix practices to organize your solution according to the business domain and create a common closure for related code from multiple services. This allows your related content resolvers and rendering code to be co-located within your solution. See the Sitecore Helix Examples repository for inspiration.

Note

To create a contents resolver, you must complete the following procedures:

  • Add the Sitecore.LayoutService NuGet package.

  • Add the Templates class.

  • Create the contents resolver class.

  • Create the contents resolver item.

  • Create the JSON rendering item.

  • Add the JSON rendering item to the standard values of the AppRoute template.

  • Test the contents resolver.

To create a model-bound view that renders the contents resolver, you must complete the following procedures:

  • Create the model.

  • Create the model-bound view.

  • Register the model-bound view.

  • Test the model-bound view.

Add the Sitecore.LayoutService NuGet package

The Sitecore.LayoutService NuGet package contains the Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver class that the contents resolver class inherits from.

To add the Sitecore.LayoutService NuGet package to the Platform project:

  1. In Visual Studio, in Solution Explorer, right-click Platform/References and click Manage NuGet Packages.

  2. In the top-right corner of the window, in the Package source menu, select the package source that points to https://nuget.sitecore.com/resources/v3/index.json.

  3. In the top-left corner of the window, click Browse and search for the Sitecore.LayoutService NuGet package.

  4. In the list of search results, click Sitecore.LayoutService.

  5. On the right side of the window, click Install to download and install the Sitecore.LayoutService NuGet package.

  6. Allow the Nuget Package Manager to modify the solution if required.

Add the Templates class

The Templates class is a dependency of the contents resolver class.

To add the Templates class to the Platform project:

  1. In Visual Studio, in Solution Explorer, right-click Platform and click Add, New Item.

  2. In the Add New Item dialog:

    • In the left pane, click Installed/Visual C#/ASP.NET Core/Code.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter Templates.cs and click Add.

  3. In the class file, add the following content:

    RequestResponse
    using Sitecore.Data;
    
    namespace MyProject
    {
      public static class Templates
      {
        public static class SiteRoot
        {
          public static readonly ID Id = ID.Parse("{061CBA15-5474-4B91-8A06-17903B102B82}");
        }
            
        public static class AppRoute
        {
          public static readonly ID Id = ID.Parse("{89829df8-93ee-56be-b57a-8f9efa6ba0a6}");
    
          public static class Fields
          {
            public static readonly ID PageTitle = ID.Parse("{09265946-b966-5034-bad7-94e0d935147a}");
          }
        }
      }
    }
  4. Save the class file.

Create the contents resolver class

The contents resolver class overrides the ResolveContents method of the Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver class and returns a content tree object.

Important

The ResolveContents method must return an object that can be returned to the rendering host as a JSON rendering.

To create the contents resolver class in the Platform project:

  1. In Visual Studio, in Solution Explorer, right-click Platform and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C#/ASP.NET Core/Code.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter NavigationContentsResolver.cs and click Add.

  3. In the class file, enter the following content:

    RequestResponse
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Sitecore.Abstractions;
    using Sitecore.Data.Items;
    using Sitecore.LayoutService.Configuration;
    using Sitecore.LayoutService.ItemRendering.ContentsResolvers;
    using Sitecore.Mvc.Presentation;
    
    namespace MyProject.LayoutService
    {
      public class NavigationContentsResolver : RenderingContentsResolver
      {
        private const int NavigationDepth = 2;
    
        private readonly BaseLinkManager _linkManager;
    
        public NavigationContentsResolver(BaseLinkManager linkManager)
        {
          _linkManager = linkManager;
        }
    
        public override object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
        {
          var contextItem = GetContextItem(rendering, renderingConfig);
          var siteRoot = contextItem?.Axes.GetAncestors().FirstOrDefault(item => item.DescendsFrom(Templates.SiteRoot.Id));
          if (siteRoot == null)
          {
            return null;
          }
    
          // First page under our site root should be Home
          return GetNavigation(siteRoot, NavigationDepth).FirstOrDefault();
        }
    
        private IEnumerable<object> GetNavigation(Item parent, int depth)
        {
          depth--;
          return parent.Children
            .Where(item => item.DescendsFrom(Templates.AppRoute.Id))
            .Select(item => new
            {
              Title = item[Templates.AppRoute.Fields.PageTitle],
              Url = _linkManager.GetItemUrl(item),
              Children = depth >= 0 ? GetNavigation(item, depth) : new object[0]
            });
        }
      }
    }
  4. Save the class file.

  5. Right-click the Platform project and click Publish.

  6. In the Platform window, click Publish to publish the Platform project in the Sitecore Content Management and Content Delivery instances.

Tip
  • Navigation contents resolvers must avoid using Sitecore.Context.Item or another page-specific context to output data on current/active navigation items (like highlighting active navigation items in the navigation menu). This is better handled client-side and ensures you can cache navigation data across pages.

  • Use Sitecore Content Search instead of Sitecore Query or other resource-expensive methods of navigating the content tree where possible.

Create the contents resolver item

The contents resolver item links the contents resolver class to Sitecore.

To create the contents resolver item in the Sitecore instance:

  1. In the Content Editor, right-click /sitecore/System/Modules/Layout Service/Rendering Contents Resolvers and click InsertRendering Contents Resolver.

    Tip

    To optimize serialization, you can organize content resolvers into folders.

  2. In the Message dialog, in the Name field, enter Navigation Resolver and click OK.

  3. On the Content tab for the Navigation Resolver item, in the Data section, in the Type field, enter the name of the contents resolver:

    RequestResponse
    MyProject.LayoutService.NavigationContentsResolver, MyProject
  4. Select Use Context Item to make the contents resolver navigate the content tree relative to the current item (the Sitecore context page/route) when building the content tree model. If you do not select this check box, the contents resolver uses its datasource item.

  5. Save the contents resolver item.

Create the JSON rendering item

The JSON rendering item renders the output of the contents resolver class (the content tree object) as JSON formatted data for the rendering host.

To create the JSON rendering item in the Sitecore instance:

  1. In the Content Editor, right-click /sitecore/Layout/Renderings and click Insert, Json Rendering.

  2. In the Message dialog, in the Name field, enter Navigation Rendering and click OK.

  3. On the Content tab for the Navigation Rendering item, in the Layout Service section, in the Rendering Contents Resolver dropdown list, click the Navigation Resolver item.

  4. Save the JSON rendering item.

Tip

If possible, configure output caching for renderings that use content resolvers. This is especially important for renderings that need access to many items, such as navigation.

Add the JSON rendering item to the standard values of the AppRoute template

By adding the JSON rendering item to the AppRoute templates standard values, all pages that inherit from the AppRoute template include the JSON rendering.

To add the JSON rendering item to the standard values of AppRoute templates:

  1. In Content Editor, click /sitecore/Templates/Project/MyProject/AppRoute/__Standard Values.

  2. On the Presentation tab, click Details.

  3. In the Layout Details dialog, in the Default section, click Edit.

  4. In the Device Editor dialog, on the Layout tab, select Layouts/Project/MyProject/Main.

  5. On the Controls tab, click Add.

  6. In the Select a Rendering dialog, click Renderings/Navigation Rendering.

  7. In the Add to Placeholder field, click Main and click Select.

  8. In the Device Editor dialog, click OK.

  9. In the Layout Details dialog, click OK.

Test the contents resolver

Before starting on the model-bound view, test that the Sitecore Layout Service includes the contents resolver JSON rendering in its output to the rendering host.

Depending on your Rendering Engine configuration, the rendering host page might output an Unknown component or a similar message. This is perfectly normal.

In this sample JSON rendering, we have added some items to the content tree to populate the navigation model:

RequestResponse
{
  "uid":"19ea6f2e-3ab9-4e9d-b3a6-9a0e5a6e9d0a",
  "componentName":"Navigation Rendering",
  "dataSource":"",
  "params":{},
  "fields":{
    "title":"Welcome to Sitecore",
    "url":"/en/",
    "children":[
      {
        "title":"Main Nav 1",
        "url":"/en/MainNav1",
        "children":[
          {
            "title":"Child Nav 1",
            "url":"/en/MainNav1/ChildNav1",
            "children":[]
          },
          {
            "title":"Child Nav 2",
            "url":"/en/MainNav1/ChildNav2",
            "children":[]
          }
        ]
      },
      {
        "title":"Main Nav 2",
        "url":"/en/MainNav2",
        "children":[
          {
            "title":"More Child Nav",
            "url":"/en/MainNav2/MoreChildNav",
            "children":[]
          }
        ]
      },
      {
        "title":"Main Nav 3",
        "url":"/en/MainNav3",
        "children":[]
      }
    ]
  }
}

Create the model

The model provides a strongly typed class that maps to the data in the contents resolver JSON rendering returned by the Sitecore Layout Service.

To create the model in the RenderingHost project:

  1. In Visual Studio, in Solution Explorer, right-click RenderingHost/Models and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C#/ASP.NET Core/Code.

    • In the middle pane, click Class.

    • At the bottom, in the Name field, enter NavigationModel.cs.

    • Click Add.

  3. In the class file, to bind the JSON rendering to the model, add the following content:

    RequestResponse
    using System.Collections.Generic;
    using Sitecore.AspNet.RenderingEngine.Binding.Attributes;
    
    namespace MyProject.Models
    {
      public class NavigationModel
      {
        [SitecoreComponentField]
        public string Title { get; set; }
    
        [SitecoreComponentField]
        public string Url { get; set; }
    
        [SitecoreComponentField]
        public IEnumerable<NavigationModel> Children { get; set; }
      }
    }
    Note

    Because you are not binding directly to Sitecore fields, you can use simple types like string in your model, but you must explicitly attribute them with [SitecoreComponentField].

  4. Save the class file.

Create the model-bound view

The model-bound view renders the model data as HTML.

To create the model-bound view in the RenderingHost project:

  1. In Visual Studio, in Solution Explorer, right-click RenderingHost/Views/Shared/Components/SitecoreComponent and click Add, New Item.

  2. In the Add New Item window:

    • In the left pane, click Installed/Visual C#/ASP.NET Core/Web.

    • In the middle pane, click Razor View.

    • At the bottom, in the Name field, enter Navigation Rendering.cshtml.

      Important

      You get an Unknown Component error if the name of the model-bound view file does not match the name of the JSON rendering.

    • Click Add.

  3. In the model-bound view file, add the following content:

    RequestResponse
    @model NavigationModel
    
    <ul>
      <li><a href="@Model.Url">Home</a></li>
      @foreach (var navigation in Model.Children)
      {
        <li>
        <a href="@navigation.Url">@navigation.Title</a>
        @if (@navigation.Children != null)
        {
          <ul>
          @foreach (var childNav in navigation.Children)
          {
            <li><a href="@childNav.Url">@childNav.Title</a></li>
          }
          </ul>
        }
        </li>
      }
    </ul>
  4. Save the model-bound view file.

Register the model-bound view

You must map the model-bound view to the layout service component name (both are called Navigation Rendering) in the Sitecore Rendering Engine.

To register the model-bound view in the Startup class:

  1. In Visual Studio, in Solution Explorer, double-click the Startup.cs file.

  2. In the Startup.cs file, in the services.AddSitecoreRenderingEngine call, register the model-bound view and the model as a Rendering Engine component with the AddModelBoundView() method:

    RequestResponse
    services.AddSitecoreRenderingEngine(options =>
      {
        //Register your components here
        options
          .AddModelBoundView<ContentBlockModel>("ContentBlock")
          .AddModelBoundView<NavigationModel>("Navigation Rendering")
          .AddDefaultPartialView("_ComponentNotFound");
      })
      // Includes forwarding of Scheme as X-Forwarded-Proto to the Layout Service so that
      // Sitecore Media and other links have the correct scheme.
      .ForwardHeaders()
      // Enable forwarding relevant headers and client IP for Sitecore Tracking and Personalization.
      .WithTracking()
      // Enable support for the Experience Editor.
      .WithExperienceEditor(options =>
      {
        // Experience Editor integration needs to know the external URL of your rendering host,
        // if behind HTTPS termination or another proxy (like Traefik).
        if (Configuration.RenderingHostUri != null)
        {
          options.ApplicationUrl = Configuration.RenderingHostUri;
        }
      });
    Important

    You get an Unknown Component error if the name of the model-bound view file does not match the name of the JSON rendering.

  3. Save the Startup.cs file.

Test the model-bound view

Tip

Do you have some feedback for us?

If you have suggestions for improving this article,