How To Make A Block Use Multiple Views ? A Partial View Controller Explained

One of the main benefits of using MVC is the separation between the view and the model.  This separation means a block or page can have more than one view.  In Episerver we have several methods available to us for rendering views.  In today's article I'm going to go discuss rendering content without a controller. When you render content within MVC that only needs a view and/or model and doesn't need the overhead of a controller, you can use something called a partial view.   When I first heard about partial views I struggled to get my head around the concept of how they worked in Episerver. If you are currently in the same boat as I was, here is a list of benefits:

  • When you have a very lightweight block like a text block, a banner, promo, image block or call to action that needs no logic to be added within view model or a controller, why waste the extra overhead creating a controller when you can simply let MVC use a default controller that will figure out which view to use.
  • Using a partial view has performance benefits as well.  When returning the view, a partial only requires a call to RenderPartial instead of RenderAction which saves a few processing cycles.

Partial Views

A partial view is a pretty easy concept to get your head around.  It's a rendering (think page or block) that is displayed without a controller.  To display a partial from a view you would use the following snippet: [code] @Html.Partial("BlockView", Model.Block) [/code] In Episerver we work with dynamic content, so it's not always as easy to define at compile time what views are needed.  This is why we need something behind the scenes that will hook up pages or blocks to the correct view for us.  When you render a block in a view it will more than likely be coming from a ContentArea using the PropertyFor helper, like so:
 @Html.PropertyFor(x => Model.PromoBlock, new
 {
    Tag = "PromoNormal"
 })
If this PromoBlock has an associated controller, the Index Action will be called in that controller and usually a view model will be created and the correct view will be called.  If our block doesn't have an associated controller, an error will be displayed that the MVC pipeline couldn't find an appropriate view for it. This means when we work with partials you will need to create a handler for partial requests.  This handler will intercept controller-less requests and point the pipeline in the right direction. Your partial views will usually live in your 'Shared' folder in your web projects 'View' folder.  You may want to create two Sub Folders called Pages and Blocks and separate out your files to make your life easier, but that's entirely optional.

Display Options

EPiServer also throws another curve ball into the partial mix as editors can also set how blocks are displayed using display options.  In Episerver you can define DisplayOptions for partials.  If this feature is enabled, a content editor will have access to a menu in episerver that will allow them to set the rendering style of the partial, this can be customised but the out of the box options are Full Width, Two Thirds, Half Width, One Third and One Fourth. episerver_display_options_dialog After enabling this feature 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

Partial View Controller

How does Episerver display a partial if it has no 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 types, if we want to, which can be really useful.
[TemplateDescriptor(TemplateTypeCategory = TemplateTypeCategories.MvcPartialController, Inherited = true,
        AvailableWithoutTag = false)]
    public class BasePartialController<T> : PartialContentController<T> where T : IContentData
    {
        public BasePartialController()
        {
        }
    }
To create a base controller you need to register it with the TemplateModelRepository, as shown below.  A partial controller needs to inherit from  PartialContentController rather than the more standard PageController for normal pages.  The other important thing to remember when using a partial controller in Episerver, is to decorate your controller with the Template Descriptor attribute. Next up we will create a default partial controller that will deal with all blocks:
    public class BlockPartialController : BasePartialController<BlockData>
    {
        private const string PARTIAL_VIEW_DIRECTORY = "~/Views/Shared/Blocks/";

        public override ActionResult Index(BlockData blockData)
        {
            var viewName = string.Empty;

            if (blockData == typeof(PromoBlock))
            {
                  viewName = "promo";
            }

            return PartialView(string.Format("{0}{1}.cshtml", PARTIAL_VIEW_DIRECTORY, viewName ));
        }
    }
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, or, we can check the type of the block and forward it on to it's associated partial.

Template Co-ordinator

So we've talked about how to define partials with a controller, next we'll talk about how to deal with partials using a template co-ordinator. The template coordinator is slightly different than a controller;  the template coordinator registers all your partial requests on application start.  The coordinator in a single location to define all of your partial mapping definitions.  To create a template coordinator, you need to declare it with a ServiceConfiguration attribute of type IViewTemplateModelRegistrator.
    [ServiceConfiguration(typeof(IViewTemplateModelRegistrator))]
    public class TemplateCoordinator : IViewTemplateModelRegistrator
    {
        public const string BlockFolder = "~/Views/Shared/Blocks/";
        public const string PagePartialsFolder = "~/Views/Shared/PagePartials/";

		public static void OnTemplateResolved(object sender, TemplateResolverEventArgs args)
		{
		}

		public void Register(TemplateModelCollection viewTemplateModelRegistrator)
		{
            viewTemplateModelRegistrator.Add(typeof (PromoBlock), new TemplateModel()
            {

                Name = "Promo",
                Tags = new[] { ContentAreaTags.FullWidth },
                AvailableWithoutTag = false,
                Path = Path = BlockPath("GamePromoNormal.cshtml")
            });
        }

        public static string BlockPath(string fileName)
        {
            return string.Format("{0}{1}", BlockFolder, fileName);
        }

		public static string PagePartialPath(string fileName)
        {
            return string.Format("{0}{1}", PagePartialsFolder, fileName);
        }
    }
    public static class ContentAreaTags
    {
        public const string FullWidth = "FullWidth";

        public const string TwoThirdsWidth = "TwoThirdsWidth";

        public const string HalfWidth = "HalfWidth";

        public const string OneThirdWidth = "OneThirdWidth";

        public const string OneFourthWidth = "OneFourthWidth";
    }
This code is very similar to the controller code, we override
void Register(TemplateModelCollection viewTemplateModelRegistrator);
In the register method, you can then define the mapping between the block being requested and the view the coordinator should send, like we did for our controller.  In the sample code below, you can see how we add our definitions into the viewTemplateModelRegistrator.  One thing you might notice from the code and that we've also yet to cover, 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:
  • PromoBlockFull
  • PromoBlockHalf
  • PromoBlockNarrow
  • PromoBlockQuarter
  • PromoBlockWide
Using the tags parameter allows us to swap between these different display options.  You can set one rule for a Full and a different rule for Half, or even a mix and match approach.

Why use Partials At All?

So you might be asking what's the benefit of this so far?  You could have just created a controller for the Promo Block, why bother creating a separate controller just to deal with one block ?  The answers simple.  From Episerver 7 onwards as well as rendering a page normally, editors can also drag blocks and pages into content areas.  Having a partial controller for a page allows us, as developers, to create different views and gives us more flexibility.  This means you can have a page that has a lot of logic and dependencies in it (say a product page() for full page rendering and a simple partial view for something like a drag and drop product spot light area.


Jon D Jones

Software Architect, Programmer and Technologist Jon Jones is founder and CEO of London-based tech firm Digital Prompt. He has been working in the field for nearly a decade, specializing in new technologies and technical solution research in the web business. A passionate blogger by heart , speaker & consultant from England.. always on the hunt for the next challenge


Back to top
var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-35662136-1']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();