In this tutorial, you will learn about Episerver partial view controllers and what you can use them for. One of the main benefits of using MVC with Episerver CMS is the improved architecture. Assuming you follow the rules, you will get a good separation of concerns between the view and the model.
One great thing about Episerver CMS is the block architecture. Blocks are reusable components that can be added to a page. When you render components within pure MVC, the terminology used is partials. You have likely come across this vanilla MVC helper,
@Html.RenderPartial(). Episerver is built on top of .NET and MVC. Episerver Blocks are basically MVC partials and they have to follow the same rendering rules. When it comes to rendering a block we have several options. If you want to learn more about these options, read on 🔥🔥🔥
Before we get too deep into this discussion, I want to start by stating my recommended approach. In my projects, I always create one controller per block. Doing this not only gives me a consistent architecture and a good level of control over how the blocks are rendered. This is not the only way that you can render blocks. If you do not want to create a controller per block, you can create a mega controller that handles all block requests. This is where a partial view controller can help.
You might be wondering when you should consider creating a block controller and when you should omit it. This is one of the reasons I always follow the rule of always creating a controller. Projects are hard, life is short, why waste time debating over small things. If all blocks have a controller it makes the code easier to pick up and reason about. It also eliminates pointless architecture conversations. This is my preference, however, to be good at your job you need to know how to fully use all the tools at your disposal, so let us think about situations where having a specific block controller might be overkill:
Blocks that need no additional logic, e.g. when you have a very lightweight block like a text block, banner, or promo block.
Blocks that contain no CMS data
Situation where you want to dynamically decide which block to run at run-time
In these instances, what is the best pattern to render these blocks?
First, let us think about how a block is rendered. A content editor, adds/creates a block in the CMS. When the page is loaded, the page controller is called. Data is passed from the page controller into the page view. When a block is rendered, about 99% of the time the request will occur within a
ContentArea using the Episerver
PropertyFor() helper, like so:
When a block render request happens, Episerver tries to find a corresponding block controller, or, if will try to find a global fallback. This is the partial controller. This is useful for us in Episerver and the CMS land. As we work with dynamic content created by content editors. It's not always possible to know at run-time what view should be rendered with a given component. Having a flexible system, allows us to add logic around which view should be rendered in real-time.
The controller look-up is done using generics. Create a controller that inherits from
BlockController and use the block you want to associate the controller with as
T. Let us say you use the type
PromoBlock in the controller, when a request is made for that block, that controller will be called, the CMS will pass in the correct CMS data and the
Index action will be called.
If the block doesn't have an associated controller, an error will be displayed that the MVC pipeline couldn't find an appropriate view for it. To stop this from happening, you will need to create a handler for partial requests. This handler will intercept controller-less requests and in it, you will need to write logic that points the pipeline in the right direction. Your partial views will usually live in the
Shared folder in your web projects
This routing logic is a lot more complex than normal MVC. The rules around partials are pretty easy to get your head around. It's a rendering (think component) that is displayed on-page. To display a partial using normal MVC you would use this snippet:
This will load a partial view file directly. Job done 🔥🔥🔥
Episerver also throws another curveball into the partial mix as editors can define how blocks are displayed using display options.
DisplayOptions allow content editors to define the width of a component. If this feature is enabled, a content editor will have access to a menu in Episerver editor that will allow them to set the rendering style of the partial. This can be customised. The ones used in the Alloy sample site are
One Third and
After enabling display options in your project, when a content editor selects the rendering size, a tag is also generated and added to the pipeline as well as the partial request. To cater for display options it is common to create one view per option. In many instances, you might create 5 views per block. Some logic needs to exist in the request pipeline to map the request to the correct view 🤔.
Partial View Controller
How does Episerver display a block request without a controller? There are a few ways we can deal with partials, the first one being a partial controller. The first thing we do is create a base partial controller. This allows us to target individual blocks if we want to. This can be really useful. The code to do this is shown below:
To create a base controller you need to register it with the
TemplateModelRepository. This registration is shown below. A partial controller needs to inherit from
PartialContentController rather than
BlockController. The other important thing to remember when using a partial controller is to decorate your controller with the
TemplateDescriptor attribute. Without this it will not work. Let us take a look at a partial controller that will deal with multiple block requests:
Our code inherits from our base controller and will capture all incoming partial requests for any blocks. In the
Index() method, we can either direct all requests to the same view, which is kinda pointless. You can check the block type and forward it to its associated partial.
The code inherits from the custom base controller. It will intercept all incoming partial requests for any blocks that do not have their own block controller. In the
Index method we can either direct all requests to the same view, which is kinda pointless or, you can check the block type and forward it on to its associated partial view. You now have a way to render a controller-less block, next we'll talk about how to register these partials using a template coordinator.
The template coordinator is used to register all your partials routing rules on application start. Within the coordinator, you define all of your partial mapping definitions. To create a template coordinator, you need to declare it with a
ServiceConfiguration attribute of type
This code is very similar to the controller code:
In the register method, you can then define the mapping between the block being requested and the view the coordinator should map to. Just like thew code we added in the partial controller. In the sample code below, you can see how we add our definitions into the
TemplateModelRegistrator.. One thing to note is the
Tags property. The
Tags property relates to the Episerver display options as discussed above. For our
Promo Block we may end up with 5 different views called:
Tags property will allow you to add some conditional logic to determine which of these views you wish to render. You can define one rule for when
Full is selected and a different view when
Half is returned. You can even go crazy and use a mix and match approach 💥 You now have a way to render a block without the need for a specific block controller.
So you might be asking what's the benefit of this so far? You could have just created a specific controller for the Promo Block with less code. Why bother creating a separate controller just to deal with one block?
The main reason why you might use this approach is page partials. Editors can also drag pages as well as blocks into content areas. When this happens the page in essence becomes a block. When a page is rendered via a
ContentArea the routing rules will be the same as a block. In this situation, the page controller for that type would not be hit. If you want to allow pages to be used in content areas, then a partial controller will allow you to enable the routing to make it happen. Confusing, but handy 😕 This post is long enough, so I will skip going into the details of page partials here. You can learn more here
In summary, the partial view controller, allows us, as developers, gives us more flexibility in how we render content. The partial view controller is usually a catch-all god controller, a controller deals with multiple things. These things can be both blocks and page partials. My preference is to create one thing per item, as I think it makes the code easier to read. I tend to avoid global controllers, so I personally do not use this approach much, however, do what makes you smile. Happy Coding 🤘