TemplateDescriptor and tags
Describes how to work with the TemplateDescriptor attribute and tags in Optimizely Content Management System (CMS), to define which template you should select for rendering in a specific context, when using multiple templates to render the same content.
TemplateDescriptor
lets you add metadata when registering templates for content types, and tags can be used, for example, to control the rendering of objects in a content area.
Note
The examples here are based on MVC.
TemplateDescriptor attribute
A page or a block can have multiple associated templates, for example, one for a web channel and one for a mobile device. Pages can also have a partial template used, for example, when you display the page inside the content area of another page.Â
TemplateDescriptor
is an optional attribute you can use to register templates and is involved when the system decides which template to use when rendering a content instance in a specific context. See Render content  and  Select templates for information on how templates are registered and selected. Use the attribute to add metadata such as template path, inheritance, content type model, and description. You can also define a default template for all content types. Â
Properties
The attribute is in the EPiServer.Framework.DataAnnotations
 namespace and some of its properties are described below.
Path
– Default: null – Set the path to the rendering template only if the folder structure does not follow the namespace structure. A namespace convention searches for the file in the path according to the namespace.ModelType
– Default: null – The model for the content type. Set this for untyped templates. It derives fromEPiServer.TemplatePage
to set the model type, which is the type of theCurrentPage
property.Default
– Default: false – Defines the template as the default template for the model type.Description
– Default: null – Contains a description of the template.Inherited
– Default: false – When set to true, model types that inherit from the ModelType get the template as a supported template.
Behavior
- If
TemplateDescriptor
is not present, template inheritance is true by default. - If
TemplateDescriptor
is present but without specified parameters, the default values specified above will apply. - If
TemplateDescriptor
is present withinherited
set to true, the template is inherited by page types based on the associated page type, which is useful if you need a fallback template for content types without specific templates.Â
Examples
Default template
The template (controller) for an Article
page type, with the TemplateDescriptor
attribute present. Inherited=false
, meaning that this is the default template for the ArticlePage
page type and no inheritance will take place.
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using EPiServer;
using EPiServer.Core;
using EPiServer.Framework.DataAnnotations;
using EPiServer.Web.Mvc;
using MyEpiserverSite.Models.Pages;
using System.Web.Security;
namespace MyEpiserverSite.Controllers {
[TemplateDescriptor(
Inherited = false,
Default = true,
Description = "Default template to be used by Article pages")]
public class ArticlePageController: PageControllerBase<ArticlePage> {
public ActionResult Index(ArticlePage currentPage) {
// Implementation of action view the page.
return View(currentPage);
}
}
}
Partial page rendering
The following example shows how to use TemplateDescriptor
for defining a partial page renderer when rendering a page inside a content area.
Assume you have the following SitePageData
base class with a summary (MetaKeywords
string) and an image:
namespace MyEpiserverSite.Models.Pages {
public abstract class SitePageData: EPiServer.Core.PageData {
[Display(GroupName = "SEO", Order = 10, Name = "Summary", Description = "Add a short content summary.")]
public virtual String MetaKeywords {
get;
set;
}
[Display(
Name = "Image", Description = "Add an image representing the content (optional)",
GroupName = "SEO",
Order = 20)]
public virtual ContentReference Image {
get;
set;
}
}
}
You have a partial page controller decorated with TemplateDescriptor
with Inherited=true
, to render page partials for pages inheriting from SitePageData
. The controller selects a view in a folder specified by the namespace convention.
namespace MyEpiserverSite.Controllers {
[TemplateDescriptor(Inherited = true)]
public class PagePartialController: PartialContentController<SitePageData> {
//
// GET: /PagePartial/
public override ActionResult Index(SitePageData currentContent) {
return PartialView("/Views/Shared/PagePartials/PagePartial.cshtml", currentContent);
}
}
}
The partial view has an if-else construct checking if the model has a template for the content. Any page inheriting from SitePageData
, will now be rendered displaying the page name with a link, the MetaKeywords
string, and an image when added to the content area of another page.
@using EPiServer.Core
@model MyEpiserverSite.Models.Pages.SitePageData
<div class="block span2">
<div class="border">
@if (Model.HasTemplate())
{
<a href="@Url.PageUrl(Model.LinkURL)">
<h3>@Model.PageName</h3>
<p>@Html.PropertyFor(x => x.MetaKeywords)<p>
<img src="@Url.ContentUrl(Model.Image)" />
</a>
}
else
{
<h3>@Model.PageName</h3>
}
</div>
</div>
Pages without controllers
An example of how you can handle page types that do not have their specific controllers by specifyinenherited=true
, and dynamically selecting template. See the Optimizely CMS (Alloy) sample site for a fully working sample.
[TemplateDescriptor(Inherited = true)]
public class DefaultPageController: PageControllerBase<SitePageData> {
public ViewResult Index(SitePageData currentPage) {
var model = CreateModel(currentPage);
return View(string.Format("~/Views/{0}/Index.cshtml", currentPage.GetOriginalType().Name), model);
}
/// <summary>
/// Creates a PageViewModel where the type parameter is the type of the page.
/// </summary>
/// <remarks>
/// Used to create models of a specific type without the calling method having to know that type.
/// </remarks>
private static IPageViewModel<SitePageData> CreateModel(SitePageData page) {
var type = typeof (PageViewModel<>).MakeGenericType(page.GetOriginalType());
return Activator.CreateInstance(type, page) as IPageViewModel<SitePageData>;
}
}
Use tags
You can apply tags to be used in the rendering selection. When a template is associated with a tag, then that template is used only when the calling context, such as Property
or PropertyFor
in a view, has a matching tag. You can also have different content areas render the same content in different ways using tags. If you have active display channels for your content types, the ChannelName
will act as a tag in the rendering selection.Â
Examples
Register multiple templates
Assume you have a model containing a teaser block with a heading and an image.
namespace MyEpiserverSite.Models.Blocks {
[ContentType]
public class TeaserBlock: BlockData {
public virtual String Heading {
get;
set;
}
public virtual ContentReference Image {
get;
set;
}
}
}
- Register two templates for the teaser block to display differently depending on the context.
- Add two sidebar templates (left and right) to display the block in the sidebar area of web pages with this.
- Add register a template for the standard block, which is part of our content model. The blocks are displayed in the content area of our web site's start page.
For performance reasons, you should use partial views directly for block types and not controllers. See Block types and templates. Because you have partial views without controllers here, use EPiServer.Web.Mvc.IViewTemplateModelRegistrator
 to register your templates. In the Business folder of your project, create a ViewTemplateModelRegistrator
class inheriting from IViewTemplateModelRegistrator
, and add the desired templates and tags.
The following code shows the template registration class using IViewTemplateModelRegistrator
:
namespace MyEpiserverSite.Business {
public class ViewTemplateModelRegistrator: IViewTemplateModelRegistrator {
public void Register(TemplateModelCollection viewTemplateModelRegistrator) {
viewTemplateModelRegistrator.Add(typeof (TeaserBlock),
new EPiServer.DataAbstraction.TemplateModel() {
Name = "SidebarTeaserRight",
Description = "Displays a teaser for a page.",
Path = "~/Views/Shared/SidebarTeaserBlockRight.cshtml",
AvailableWithoutTag = true
},
new EPiServer.DataAbstraction.TemplateModel() {
Name = "SidebarTeaserLeft",
Description = "Displays a teaser for a page.",
Path = "~/Views/Shared/SidebarTeaserBlockLeft.cshtml",
Tags = new string[] {
RenderingTags.Sidebar
}
});
viewTemplateModelRegistrator.Add(typeof (StandardBlock),
new EPiServer.DataAbstraction.TemplateModel() {
Name = "SidebarTeaser",
Description = "Displays a teaser of a page.",
Path = "~/Views/Shared/StandardBlock.cshtml",
Tags = new string[] {
RenderingTags.Sidebar
}
});
}
}
}
The following code shows the SidebarTeaserBlockRight
partial view for the teaser block (there is an identical SidebarTeaserBlockLeft
 for the left one):
@model MyEpiserverSite.Models.Blocks.TeaserBlock
<div>
<h2>@Html.PropertyFor(x => x.Heading)</h2>
<img src="@Url.ContentUrl(Model.Image)" />
</div>
 The following code shows the rendering view for the start page, where the blocks are displayed:
@model MyEpiserverSite.Models.Pages.StartPage
<div>
@Html.PropertyFor(m => m.MainBody)
</div>
<div>
@Html.PropertyFor(m => m.GeneralContentArea, new { Tag = EPiServer.Framework.Web.RenderingTags.Sidebar })
</div>
How to use tagging in a template selection:
- If you set theÂ
AvailableWithoutTag
 attribute for a template to true, it applies the template regardless of whether the rendering context has a corresponding tag. - If theÂ
AvailableWithoutTag
 is set to false, or does not exist, the template is not applied unless the rendering context has a corresponding tag.
In this scenario, the following happens:
- The start page
GeneralContentArea
has theRenderingTags.Sidebar
 tag; it applies to only templates with this tag, without theAvailableWithoutTag
, or where this is set tofalse
. - TheÂ
SidebarTeaserLeft
template has a matching tag andAvailableWithoutTag
set, and is applied. This is also valid for the template used for the Standard block. - The
SidebarTeaserRight
template has a matching tag, andAvailableWithoutTag = true
. It applies the template even if you remove theRenderingTags.Sidebar
tag from the content area.
Updated 6 months ago