Pages

Monday, October 18, 2021

Rendering ContentReference With Shared Views Using a TemplateTag

ContentReference properties provide a way to select a single item in Optimizely CMS when you don't need the featureset a ContentArea provides. However, rendering an item with a ContentReference is a bit different. I explored this a bit in my article Rendering ContentReference Properties. This post expands on this with a single DisplayTemplate that works for most content types. It builds on a similar technique using a ContentReferencePartial, and adds additional functionality using a TemplateTag.

The ContentReferencePartial approach

The ContentReferencePartial approach uses a DisplayTemplate to load the content type and render it using a HtmlHelper. The original inspiration for this comes from this forum post. One problem with the original post is it only works with the default view for a block. Sometimes, though, you have different partials you want to render for a block type based on where it's located in a page. For instance, in the image below, the content in the main body and on the sidebar are the same block, but use different view partials.

With a ContentArea you accomplish this by specifying a Tag with the PropertyFor helper. With the original approach for a ContentReference, the Tag specifies the DisplayTemplate instead. The approach below allows the ContentReferencePartial idea to work like the ContentArea by adding a TemplateTag.

Start by creating the helper extension. 

This is the piece that loads the content data and renders it based on the type.

[pre class="brush:csharp;class-name:collapse-box;"]
using System.Web.Mvc;
using EPiServer;
using EPiServer.Core;
using EPiServer.ServiceLocation;
using EPiServer.Web.Mvc.Html;

namespace BaseEpiserverSite.Extensions
{
    public static class HtmlHelpers
    {
        public static void RenderContent(this HtmlHelper helper, ContentReference contentRef, string templateTag = "")
        {
            var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
            if (contentLoader.TryGet(contentRef, out IContentData contentData))
            {
                helper.RenderContentData(contentData, false, templateTag);
            }
        }
    }
}
[/pre]

Next, create the ContentReferenceRenderer in your DisplayTemplates directory.

[pre class="brush:razor" title="ContentReferenceRenderer.cshtml"]
@using BaseEpiserverSite.Extensions
@using EPiServer.Core
@model ContentReference

@*This partial view makes it simple to display blocks stored in ContentReference objects using their defined partial.
    By default, a ContentReference would render a text link instead.*@
@if (!ContentReference.IsNullOrEmpty(Model))
{
    var templateTag = (ViewData["TemplateTag"] ?? "").ToString();

    Html.RenderContent(Model, templateTag);
}
[/pre]

Finally, add your TemplateModel using the ViewTemplateModelRegistrator

[pre class="brush:csharp;class-name:collapse-box;"]
using BaseEpiserverSite.Models.Blocks;
using EPiServer.ServiceLocation;
using EPiServer.Web.Mvc;

namespace BaseEpiserverSite.Initialization
{
    [ServiceConfiguration(typeof(IViewTemplateModelRegistrator))]
    public class ViewTemplateModelRegistrator : IViewTemplateModelRegistrator
    {
        public void Register(TemplateModelCollection viewTemplateModelRegistrator)
        {
            viewTemplateModelRegistrator.Add(typeof(TeaserBlock), new EPiServer.DataAbstraction.TemplateModel
            {
                Name = "SmallSidebarTeaser", // this is for reference purposes
                AvailableWithoutTag = false,
                Inherit = true,
                Tags = new[] { "SmallTeaserTesting" },  // this is the Tag data
                Path = "SmallTeaserTesting" // this can be just a view name
            });
        }
    }
}
[/pre]

 

To use this with the PropertyFor helper you add a Tag data for ContentReferenceRenderer and a TemplateTag data for the TemplateModel tag. In this example the TemplateTag is SmallTeaserTesting.

[pre class="brush:razor;class-name:collapse-box"]
<div class="sidebar-content">
    <h4>This is my sidebar</h4>
    @Html.PropertyFor(m => m.CurrentPage.SidebarFeaturedItem, new { Tag = "ContentRefRender", TemplateTag = "SmallTeaserTesting" })
    <p>Test content under the Sidebar Teaser</p>
</div>
[/pre]

 

This works great when you have a bunch of TemplateModel or existing partials and want to use them with ContentReference properties. You can also omit the TemplateTag to simply render the default view for the content.

No comments:

Post a Comment

Share your thoughts or ask questions...