Editorial template control in Episerver? Also, what's `IDynamicTemplateContent`?
- Page Owner: Not Set
- Last Reviewed: 2021-05-21
How do I let editors in Episerver select the which template a block renders with? I want something like a dynamic template.
Answer
We use a system called IDynamicTemplateContent. It will provide editors with a list of possible templates (through a dropdown property that you create that lists all the registered views available).
Here's what you do:
- Add the
IDynamicTemplateContentinterface to your block model. - Add a property for the template selection. For example:
[CultureSpecific]
[Display(Name = "Style", Order = 180, Description = "Optional multiple visual variations, adjusted by adding a CSS class to the object container.", GroupName = CmsGroupNames.Content)]
[UIHint("TemplateModel")]
public virtual string CTABlockStyle { get; set; }
- Implement
IDynamicTemplateContentlike so:
public void SetDynamicTemplate(TemplateResolverEventArgs args)
{
if (!this.CTABlockStyle.HasValue() || SelectedTemplateIsPreviewController(args.SelectedTemplate))
{
return;
}
TemplateModel selectedTemplate = args.SupportedTemplates
.FirstOrDefault(tmpl => tmpl.TemplateType.FullName.Equals(this.CTABlockStyle, StringComparison.InvariantCultureIgnoreCase));
if (selectedTemplate != null)
{
args.SelectedTemplate = selectedTemplate;
}
}
private bool SelectedTemplateIsPreviewController(TemplateModel selectedTemplate) =>
selectedTemplate != null && selectedTemplate.Tags != null && selectedTemplate.Tags.Contains(RenderingTags.Preview);
You will need some supporting classes in your project.
The editor descriptor:
[EditorDescriptorRegistration(TargetType = typeof(string), UIHint = "TemplateModel")]
public class TemplateModelEditorDescriptor : EditorDescriptor
{
private readonly ITemplateRepository templateModelRepository;
public TemplateModelEditorDescriptor()
: this(ServiceLocator.Current.GetInstance<ITemplateRepository>())
{
}
public TemplateModelEditorDescriptor(ITemplateRepository templateModelRepository)
{
this.templateModelRepository = templateModelRepository ?? throw new ArgumentNullException("templateModelRepository");
ClientEditingClass = "epi-cms/contentediting/editors/SelectionEditor";
}
public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
{
base.ModifyMetadata(metadata, attributes);
metadata.CustomEditorSettings["uiType"] = metadata.ClientEditingClass;
metadata.CustomEditorSettings["uiWrapperType"] = UiWrapperType.Floating;
var contentType = metadata.FindOwnerContent()?.GetOriginalType();
TemplateTypeCategories[] validTemplateTypeCategories;
if (typeof(IRoutable).IsAssignableFrom(contentType))
{
validTemplateTypeCategories = new[] {
TemplateTypeCategories.MvcController,
TemplateTypeCategories.WebFormsPage,
TemplateTypeCategories.WebForms,
TemplateTypeCategories.MvcView,
TemplateTypeCategories.Page
};
}
else
{
validTemplateTypeCategories = new[] {
TemplateTypeCategories.MvcPartialController,
TemplateTypeCategories.MvcPartialView,
TemplateTypeCategories.WebFormsPartial,
TemplateTypeCategories.ServerControl,
TemplateTypeCategories.UserControl
};
}
var templates = this.templateModelRepository.List(contentType);
var templateModels = templateModelRepository
.List(contentType)
.Where(x => Array.IndexOf(validTemplateTypeCategories, x.TemplateTypeCategory) > -1);
metadata.EditorConfiguration["selections"] = templateModels
.Select(x => new SelectItem
{
Text = this.GetDisplayName(x.TemplateType, x.Name),
Value = x.TemplateType.FullName // Value stored in the database
}).OrderBy(x => x.Text);
}
private string GetDisplayName(Type contentType, string name)
{
string displayName = name.Coalesce(contentType.Name);
if (displayName.StartsWith("Default"))
{
displayName = "Default";
}
else if(displayName.Contains("-"))
{
displayName = displayName.Substring(displayName.LastIndexOf("-") + 1);
}
return displayName;
}
}
The initialization module:
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class DynamicTemplateInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
context.Locate.TemplateResolver().TemplateResolved += OnTemplateResolved;
}
public void Uninitialize(InitializationEngine context)
{
context.Locate.TemplateResolver().TemplateResolved -= OnTemplateResolved;
}
private static void OnTemplateResolved(object sender, TemplateResolverEventArgs args)
{
if (args.ItemToRender is IDynamicTemplateContent content)
{
content.SetDynamicTemplate(args);
}
}
}
And of course the IDynamicTemplateContent interface:
public interface IDynamicTemplateContent
{
void SetDynamicTemplate(TemplateResolverEventArgs args);
}
All of this code was taken from the RW Baird project. For now, check there for the most up-to-date version of this code.