HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

TemplateDescriptor

Describes how to work with the TemplateDescriptor attribute 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 you can use tags to control the rendering of objects in a content area.

📘

Note

The examples are based on ASP.NET Core.

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 the page is displayed inside the content area of another page.

TemplateDescriptor is an optional attribute that registers templates and is involved when the system determines 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.

Properties

The attribute is found in the EPiServer.Framework.DataAnnotations namespace and some of its properties are described below.

  • Path – The path to the rendering template must only be set if the folder structure does not follow the standard MVC convention. The default is null.
  • ModelType – The model to which the template applies. The model type can be set to an interface or abstract type if the template should be registered for multiple concrete types. The default is null.
  • Description – Contains a description of the template. The default is null.
  • Inherited – When set to true, model types that inherit from the ModelType get the template as a supported template. The default is false.
  • Tags – An optional list of tags for which this template is registered.
  • AvailableWithoutTag – Specifies if this template is available when tags defined in Tags are not present in context.
  • TemplateTypeCategory – Optional value to specify the template type. If not specified, the template type is resolved from ModelType.

Behavior

If TemplateDescriptor is:

  • not present, template inheritance is true by default.
  • present but without specified parameters, the default values above apply.
  • present with inherited set to true, the template is inherited by types assignable to ModelType. This is useful if you need a fallback template for content types without specific templates.

Concrete type example

Assume you have a ArticlePage content type :

using EPiServer.Core;

namespace MyOptimizelySite.Models.Pages {
  [ContentType]
  public class ArticlePage: PageData {
    public virtual string Heading {
      get;
      set;
    }
  }
}

The template (controller) for the ArticlePage content type, with the TemplateDescriptor attribute present. Inherited is set to false, meaning that this template is used as the default template for the ArticlePage content type and no inheritance will take place.

using EPiServer;
using EPiServer.Core;
using EPiServer.Framework.DataAnnotations;
using EPiServer.Web.Mvc;
using MyOptimizelySite.Models.Pages;

namespace MyOptimizelySite.Controllers {
  [TemplateDescriptor(
    Inherited = false,
    Description = "Default template to be used by Article pages")]
  public class ArticlePageController: PageController<ArticlePage> {
    public ActionResult Index(ArticlePage currentPage) {
      // Implementation of action view the page. 
      return View(currentPage);
    }
  }
}

Partial page rendering example

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:

using EPiServer.Core;

namespace MyOptimizelySite.Models.Pages {
  public abstract class SitePageData: PageData {
    public virtual string MetaKeywords {
      get;
      set;
    }
    public virtual ContentReference Image {
      get;
      set;
    }
  }
}

You have a partial page component 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.

using AlloyTemplates.Models.Pages;
using EPiServer.Framework.DataAnnotations;
using EPiServer.Web.Mvc;
using Microsoft.AspNetCore.Mvc;

namespace MyOptimizelySite.ViewComponents {
  [TemplateDescriptor(Inherited = true)]
  public class PagePartialComponent: PartialContentComponent<SitePageData> {
    protected override IViewComponentResult InvokeComponent(SitePageData currentContent) {
      return View("/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 the SitePageData is 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 SitePageData
    
<div class="block span2">
  <div class="border">
    @if (Model.HasTemplate())
    {
      <a href="@Url.ContentUrl(Model.ContentLink)">
        <h3>@Model.PageName</h3>
        <p epi-property="@Model.MetaKeywords"><p>
        <img src="@Url.ContentUrl(Model.Image)" />
      </a>
    }
    else
    {
      <h3>@Model.PageName</h3>
    }
  </div>
</div>

Pages without controllers example

An example of how you can handle page types that do not have their specific controllers by specifying Inherited=true, and dynamically selecting template. See Install a sample site for a fully working sample.

[TemplateDescriptor(Inherited = true)]
public class DefaultPageController: PageController<SitePageData> {
  public ViewResult Index(SitePageData currentPage) {
    return View($"~/Views/{currentPage.GetOriginalType().Name}/Index.cshtml", currentPage);
  }
}