I gotta be honest - my first few attempts to understand how Page Templates work in Kentico 12 MVC resulted in utter failure ๐.
They're not quite traditional MVC renderings and they're not fully Page Builder components.
Instead they're a cool and weird hybrid with a multi-step process for handling an HTTP request ๐ต.
What are MVC Page Templates? What kinds of scenarios are they good for? How do they work behind the scenes? How do I get started with them?
In this post I'll attempt to give helpful answers for all these questions ๐.
What are Kentico 12 MVC Page Templates?
I've found the easiest way to think of Page Templates is this:
Page Templates let the content managers select an MVC View to render a Page in the content tree.
That's it! End of the blog post!
...
Just kidding ๐.
But really, that's about all there is to it on a conceptual level.
In the screen capture below, I'm creating a new Page that uses MVC Page Templates. When choosing one of the "Default templates", I'm really choosing an MVC View.
MVC Page Templates give developers a way to enable and present dynamic View selection to content managers through a standard pattern with a nice UI.
Of course, MVC Page Templates include lots of other features and functionality ๐ช๐ฝ, but their main purpose is to provide a predefined set of variations for rendering a Page's content.
When Would We Use MVC Page Templates?
The most obvious use case for MVC Page Templates is to change the layout of a page's content - repositioning that content in different parts of the page depending on the Template selected.
However we could also use them when we want to completely change the code of the page by building Page Templates that use different MVC _Layout.cshtml
files, with different CSS, JavaScript, and MVC Partials or Child Actions ๐ค.
To add to this scenario, there's a perfectly valid use-case for Page Templates combined with Pages that have little, or no, content themselves. Instead, we rely on Editable Areas to provide content managers with locations in the layout where they can add any number of MVC Widget Sections and Widgets ๐ค.
These Editable Areas can then be re-positioned with the different layouts in each Page Template.
What Other Features Do MVC Page Templates Have?
While selecting an MVC View for a given Page is a pretty compelling feature, Page Templates also come with a couple other neat perks ๐.
Page Template Properties
Page Templates can have custom and configurable properties, just like MVC Widgets and Widget Sections.
This means we can customize the look and feel of a page after having selected a specific Page Template for the Page.
In the screen capture below you can see how I use the Page Template properties icon to open the properties dialog and change the "Show Hero" value.
Generally, enabling content managers to change the structure (layout) of a page should be accomplished by providing multiple MVC Page Templates โ.
A good example would be whether the sidebar of a page in on the left or right. Or switching between a full page width layout compared to one with a constrained and centered content column.
On the other hand, exposing smaller, non-structural, cosmetic options for a Page can be done through MVC Page Template Properties โ.
These cosmetic changes might be setting the background color of a Page, or specifying how many images to display in a gallery.
Page Template Filtering
Page Templates are registered globally in the content delivery (MVC) application. But not all Page Templates are applicable to all Pages on the site ๐คจ.
We can use Page Template Filters to restrict a Page Template to Pages of a specific type.
These filters work in much the same way as specifying which MVC Widgets are allowed to be used in a given Editable Area ๐๐พ.
Custom Page Templates
MVC Page Templates are MVC Views, and when rendered, work just like for any other normal page ๐ค.
This means we can include Editable Areas in them so that content managers can added Widget Sections and Widgets to the Page, which leads to one of the most interesting features of Page Templates...
... the ability to save the Page Builder configuration, added to a Page Template Page, as a Custom Page Template ๐ฎ.
Below you can see how I change the Page Template properties, by hiding the hero image, add a Content Widget, and then save everything as a Custom Page Template named "Landing Page with Content".
Switching back to the 2 Default templates restores the Page to the default state for those templates, but changing to the custom "Landing Page with Content" template loads the Page Template property and Widget state that I previously saved ๐คฏ.
How Do Custom Page Templates Work?
The Widget configuration for a given page is stored in the CMS_Document.DocumentPageBuilderWidgets
column and the configuration for a given page's Page Template is stored in the CMS_Document.DocumentPageTemplateConfiguration
column.
When creating a Custom Page Template, a new row is created in the CMS_PageTemplateConfiguration
table and both of the above CMS_Document
columns for a Page are copied to the PageTemplateConfigurationWidgets
and PageTemplateConfigurationTemplate
columns respectively.
Then, when a new Page is created using that Custom Page Template, those CMS_PageTemplateConfiguration
table column values get copied to the CMS_Document
columns for the new Page ๐ง.
There currently isn't a way to update an existing Custom Page Template ๐, we but we can always create new ones from an existing Page ๐คท๐ฟโโ๏ธ.
When selecting a Custom Page Template while creating a new Page, what we are really doing is selecting a Page Template that we've registered in code, combined with a snapshot of Page Template property values and Widget/Widget Sections with their properties ๐ฎ.
We aren't ever storing any reference to the Custom Page Template as part of Page's values - it's just a copy/paste.
This means we're safe to delete any Custom Page Templates without affecting any existing Pages ๐๐ฝ.
However, removing a Page Template registration from our code for an in-use Page Template would affect our existing Pages - they'd throw and exception and fail to render ๐จ!
If we navigate to the "Page Template (MVC)" module in Kentico, we can see a list of all the Custom Page Templates we've created.
The functionality is pretty limited here, with deletion being the only real meaningful action.
Coding an MVC Page Template Controller
Let's look at the code used in the above screen captures to get an idea of what is required to create MVC Page Templates.
We're going to use Controller-based Page Templates, as they show more of the API and setup, rather than the simpler View-only Page Templates.
MVC Controller
First, we need a normal MVC Controller that is routed to when the Page Template Page is visited:
public class LandingPageController : Controller
{
public ActionResult Index()
{
var page = DocumentHelper
.GetDocuments()
.Path("/Landing-Page")
.OnCurrentSite()
.TopN(1)
.FirstOrDefault();
return page is null
? HttpNotFound() as ActionResult
: new TemplateResult(page.DocumentID);
}
}
The key here is returning a new TemplateResult()
with the DocumentID
of the Page using Page Templates.
Page Template Controllers
Next, we define some Page Template Controllers. Our first doesn't have any defined properties, so it's really simple:
public class LandingPageBasicTemplateController
: PageTemplateController
{
public ActionResult Index() =>
View("_BasicTemplate");
}
We inherit from PageTemplateController
and define a normal Index()
action method that returns the View defined for this Page Template.
This part is key ๐ to understanding how to implement MVC Page Templates. The first Controller (
LandingPageController
) receives the request and allows Kentico to intercept it by returning aTemplateResult
.Then, based on the Page Template selected, Kentico calls the correct
PageTemplateController
to render the selected Page Template (MVC View).When using Page Templates with custom Controllers or properties, we will always have at least 2 Controllers - 1 for the route and 1 for the rendering ๐.
Our second Page Template Controller has the "Show Hero" property, so the implementation is slightly more complex:
public class LandingPageHeroTemplateController :
PageTemplateController<LandingPageHeroTemplateProperties>
{
public ActionResult Index() =>
View("_HeroTemplate", new LandingPageHeroTemplateViewModel
{
ShowHero = GetProperties().ShowHero
});
}
public class LandingPageHeroTemplateProperties :
IPageTemplateProperties
{
[EditingComponent(
CheckBoxComponent.IDENTIFIER,
Label = "Show Hero")]
public bool ShowHero { get; set; } = true;
}
public class LandingPageHeroTemplateViewModel
{
public bool ShowHero { get; set; }
}
We define a Controller class that inherits PageTemplateController<T>
where T
is a class implementing IPageTemplateProperties
.
The GetProperties()
method comes from PageTemplateController<T>
.
The Page Template properties class works the same as properties classes for Widgets or Widget Sections ๐.
Finally we define a view model to pass our property value from the Controller to the View ๐๐พ.
Page Template Views
The Views for our 2 Page Templates are simple.
Note the location for the View files for both Controllers.
Although we are using 2 new Controller classes to render the Page Template views, the MVC request is being handled in the context of the
LandingPageController
.Therefore, the files, following MVC convention, are located under
~/Views/LandingPage
๐ค:
<!-- ~/Views/LandingPage/_BasicTemplate.cshtml -->
<div class="section">
<div class="row">
<div class="col d-flex justify-content-center">
<p>
This is a landing page basic template.
</p>
</div>
</div>
</div>
@Html.Kentico().EditableArea("landing-page-bottom")
and
<!-- ~/Views/LandingPage/_HeroTemplate.cshtml -->
@model Sandbox.Controllers.LandingPageHeroTemplateViewModel
@if (Model.ShowHero)
{
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4">Fluid jumbotron</h1>
<p class="lead">Lorem Ipsum.</p>
</div>
</div>
}
<div class="section">
<div class="row">
<div class="col d-flex justify-content-center">
<p>
This is a landing page hero template.
</p>
</div>
</div>
</div>
@Html.Kentico().EditableArea("landing-page-bottom")
Page Template Registration
The final bit of code are the [RegisterPageTemplate()]
assembly attributes that register our Page Templates with Kentico so that they appear in the Page Template selection dialog:
[assembly: RegisterPageTemplate(
"sandbox.landing-page.hero",
typeof(LandingPageHeroTemplateController),
"Landing Page Hero")]
[assembly: RegisterPageTemplate(
"sandbox.landing-page.basic",
typeof(LandingPageBasicTemplateController),
"Landing Page Basic")]
Copying this code into your Kentico 12 MVC application should have you up and running with some cool ๐ MVC Page Templates that you can play around ๐๐พ with and enhance.
Conclusion
So, now we've peered deep ๐พ into the beautiful abyss of Kentico 12 MVC Page Templates.
We've defined what they really are - a convenient way to let content managers pick the MVC View for rendering content - and the various common use-cases we might implement them for.
We've also covered the additional features that Page Templates bring to our sites - properties, filtering, and Custom Page Templates.
We even looked at how Kentico stores Page Template data and creates new Custom Page Templates in the database ๐ค.
Finally, we stepped through the process of creating some simple Page Templates for our Landing Page.
I hope this overview of MVC Page Templates helps clarify any confusion you've had about this cool ๐ technology (I wish I had a blog post like this to read when I was trying to figure them out ๐ !)
As always, thanks for reading ๐!
We've put together a list over on Kentico's GitHub account of developer resources. Go check it out!
If you are looking for additional Kentico content, checkout the Kentico tag here on DEV:
Or my Kentico blog series:
Top comments (2)
Yeah, I definitely found the oddest part of this piece that the MVC page templates (which as you noted, are basically Page -> View + Model + Controller mappings, and stay associated with the page) and Custom Page Templates (which are more like 'Page Template Choice + Widget Pre-fill', and are just used when creating a page, they don't stay associated) are both called 'page templates' and seem otherwise unrelated. I would love to see the latter rebranded to something like 'Widget Page Presets'
That definitely seems like a much better name!
If the "Save as Template" option in the Page UI had instead said "Save as Page Template Preset" or "Save as Preset" it wouldn't have been so confusing.
The "preset" does include the Page Template properties in addition to Widgets and Widget Sections - maybe calling this "Page Builder Configuration" is more holistic?
I spent a fair bit of time watching the database for changes after each interaction I had with Page Templates to see what a effect a UI click would have. It wasn't clear at all from the documentation which, to be fair, is aimed getting code working rather than explaining the details of how it works.