Content Area Rendering in Episerver

In a recent project, we were set the challenge of building a new public website for a client in Episerver CMS. The advantages of doing this were around how configurable content types were in Episerver compared to some of the alternatives we might have been asked to use. We thought it might be useful to describe some of the lessons we learned around content area rendering in this blog post.

What is Episerver CMS?

Episerver CMS is a content management system created using Microsoft technology stack ASP.NET. It is used by a multitude of companies to create, edit and publish content for their websites and intranets.

Episerver differs from other CMSes, like WordPress, in its complexity. Instead of providing users with preprogrammed units and ready-to-use features, Episerver is a framework that allows for innumerable extensions and modifications. This framework is one of the very best features in Episerver. Its ability to have virtually every bit of it extended and modified enables development in Episerver to tailor websites to user needs more accurately.

Add to this Episerver’s ability to integrate with other systems because it is specifically designed to handle such integrations, and Episerver proves itself to be the best content management system for many website development projects.

Developing in Episerver

Episerver’s development flexibility turned out to be one of the greatest assets in a recent project. We had some rather complex requirements – a set of content blocks that could (1) be used on any page and (2) have a certain behaviour if they were the first item in a content area of one page-type, but (3) not others. There are numerous ways to deal with this. However, we decided that the best solution would be to create something that suited our generic, reusable convention-oriented approach. Episerver was the best content management system for the job to ensure a similar experience for both visitors and editors, as well as maintaining consistency between views.

We had the following two primary requirements:

Pass in custom attributes to both content area as well as its child items from the view that is rendering the property

Allow each item in the content area to have its own attributes depending on varying criteria, such as the item’s content type or its position in the content area.

We implemented these requirements by updating the default Alloy template and creating a tag builder convention which replicates the behaviour of the Alloy renderer performing these actions:

Determine the rendering tag provided for that specific item or its container and provide relevant class

Fetch and add a custom CSS class if the item implements `CustomCssInContentArea`

Content area renderer

Let’s start by clearing out AlloyContentAreaRenderer and add the following methods and stubs:

The Render(HtmlHelper, ContentArea) method is pretty much the original implementation. Unfortunately, we don’t have another method we could use to access the content area’s own TagBuilder instance before being written into the response stream.

Storing content area data

In order to carry around information between content area items as they’re being rendered, we need a context within which we can store those details. We’re able to make this easier for ourselves by understanding the dependency resolution lifecycle rules for deciding when to create a new instance – the original ContentAreaRenderer is registered with a Unique lifecycle, so every time its resolution is requested, we get a new instance. This means that every time PropertyFor() or DisplayFor() is called in the view for a content area, eventually there’s a request to resolve a ContentAreaRenderer and we know each ContentArea instance rendered that way will have its own ContentAreaRenderer instance. While this also means we could store all our data publicly in renderer and just pass its instance around, we’d ultimately have a serious violation of the Single Responsibility Principle (SRP).

Avoiding external dependencies

Since we want to keep the context as clean as possible from external dependencies, we’ll ensure that all items are populated by the time they’re needed. To do this, we’ll also wrap the call to properly render the content area item in between calls to BeginRenderingItem() and FinishRenderingItem() methods:

The first interface is to hold our registry of conventions. This may be required to be generated in a different manner, or may obtain its conventions from a different source, or perhaps order them by an attribute. Neither the conventions nor consuming renderer will need to care about where one or the other comes from – that is the responsibility of the dependency inversion container.

The first interface has an easy enough implementation, it’ll just take an enumerable of types of the second interface as dependencies and loop through them to apply when requested:

We’ll add the composer interface as an additional dependency to the AlloyContentAreaRenderer class. We’ll make use of it in Render(HtmlHelper, ContentArea) method override’s if-statement and as the action for BeforeRenderContentAreaItemStartTag(TagBuilder, ContentAreaItem) method:

While the current approach assumes the responsibility to determine whether the TagBuilder instance passed to it belongs to the container or to a child item to the convention implementation, it would be possible to either create and implement two more interfaces. An example of this is IContentAreaConvention and IContentAreaConventionComposer and using those for appropriate calls. In the interests of brevity, we won’t carry out those additional steps in this post.

Replicating the original Alloy renderer behaviour

That leaves only two more things left to do – first, the convention which replicates the original Alloy renderer behaviour:

Second, and before any of this will work, we need to register the required classes in an implementation of IConfigurableModule. With Alloy, there is already a DependencyResolverInitialization which will work. The initialization code might look something like this (includes a helper class at the end):

The project can now be run and assuming Alloy base, the site should look exactly the same as the unmodified Alloy sample site.

The singleton registration can be changed in the initialisation module if there is a need for dependency injection in the conventions themselves. In the interest of performance, we recommend the rules for class names to be decided in such a way which doesn’t require dynamic access for individual content area items other than viewdata or what is already in the rendering context.

The convention pay off

To examine other ways of using the convention here are a couple of example implementations:

We found Episerver to be extremely powerful and it met our needs really well. However, given its complexity, it can be a steep learning curve. If you are trying to settle on a good way to configure the rendering of blocks in Episerver, hopefully, you have found our experiences and examples helpful.