loading...

Kentico 12: Design Patterns Part 25 - MVC Page Templates

seangwright profile image Sean G. Wright ใƒป8 min read

Kentico 12 - Design Patterns (25 Part Series)

1) Kentico 12: Design Patterns Part 1 - Writing Testable Code 2) Kentico 12: Design Patterns Part 2 - Writing Unit Tests 3 ... 23 3) Kentico 12: Design Patterns Part 3 - Tips and Tricks, Application Structure 4) Kentico 12: Design Patterns Part 4 - Adding Dependency Injection to the CMS 5) Kentico 12: Design Patterns Part 5 - Front-End Dependency Management 6) Kentico 12: Design Patterns Part 6 - Rendering Meta Tags in Kentico 12 MVC 7) Kentico 12: Design Patterns Part 7 - Integrating Web API 2 8) Kentico 12: Design Patterns Part 8 - Setting Up Integration Tests 9) Kentico 12: Design Patterns Part 9 - The Different Ways to Store Content in Kentico 12 MVC 10) Kentico 12: Design Patterns Part 10 - MVC Routing with NodeAliasPath 11) Kentico 12: Design Patterns Part 11 - Unit Testing Custom Page Types 12) Kentico 12: Design Patterns Part 12 - Database Query Caching Patterns 13) Kentico 12: Design Patterns Part 13 - Generating Page URLs 14) Kentico 12: Design Patterns Part 14 - DocumentQuery and ObjectQuery Tips 15) Kentico 12: Design Patterns Part 15 - Output Caching and User Context 16) Kentico 12: Design Patterns Part 16 - Integrating Vue.js with MVC 17) Kentico 12: Design Patterns Part 17 - Centralized Cache Management through Decoration 18) Kentico 12: Design Patterns Part 18 - Preparing for Kentico 2020 19) Kentico 12: Design Patterns Part 19 - Protecting An API Against XSRF 20) Kentico 12: Design Patterns Part 20 - Choosing a Solution Architecture 21) Kentico 12: Design Patterns Part 21 - MVC Widget Tips 22) Kentico 12: Design Patterns Part 22 - Improving Our Projects for Developer Experience 23) Kentico 12: Design Patterns Part 23 - Improving Our Projects with Documentation 24) Kentico 12: Design Patterns Part 24 - Improving Our Projects with Configuration 25) Kentico 12: Design Patterns Part 25 - MVC Page Templates

A blank notepad and watercolor paints and brushes

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.

Creating a new page in Kentico using Page Templates

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.

Toggling a Page Template's property on and off

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".

Creating a new Custom Page Template and toggling between the default Templates and the custom one

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.

A list of the single "Landing Page with Content" Custom Landing Page

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 a TemplateResult.

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 ๐Ÿ™!


Photo by Tim Arterbury on Unsplash

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:

Kentico 12 - Design Patterns (25 Part Series)

1) Kentico 12: Design Patterns Part 1 - Writing Testable Code 2) Kentico 12: Design Patterns Part 2 - Writing Unit Tests 3 ... 23 3) Kentico 12: Design Patterns Part 3 - Tips and Tricks, Application Structure 4) Kentico 12: Design Patterns Part 4 - Adding Dependency Injection to the CMS 5) Kentico 12: Design Patterns Part 5 - Front-End Dependency Management 6) Kentico 12: Design Patterns Part 6 - Rendering Meta Tags in Kentico 12 MVC 7) Kentico 12: Design Patterns Part 7 - Integrating Web API 2 8) Kentico 12: Design Patterns Part 8 - Setting Up Integration Tests 9) Kentico 12: Design Patterns Part 9 - The Different Ways to Store Content in Kentico 12 MVC 10) Kentico 12: Design Patterns Part 10 - MVC Routing with NodeAliasPath 11) Kentico 12: Design Patterns Part 11 - Unit Testing Custom Page Types 12) Kentico 12: Design Patterns Part 12 - Database Query Caching Patterns 13) Kentico 12: Design Patterns Part 13 - Generating Page URLs 14) Kentico 12: Design Patterns Part 14 - DocumentQuery and ObjectQuery Tips 15) Kentico 12: Design Patterns Part 15 - Output Caching and User Context 16) Kentico 12: Design Patterns Part 16 - Integrating Vue.js with MVC 17) Kentico 12: Design Patterns Part 17 - Centralized Cache Management through Decoration 18) Kentico 12: Design Patterns Part 18 - Preparing for Kentico 2020 19) Kentico 12: Design Patterns Part 19 - Protecting An API Against XSRF 20) Kentico 12: Design Patterns Part 20 - Choosing a Solution Architecture 21) Kentico 12: Design Patterns Part 21 - MVC Widget Tips 22) Kentico 12: Design Patterns Part 22 - Improving Our Projects for Developer Experience 23) Kentico 12: Design Patterns Part 23 - Improving Our Projects with Documentation 24) Kentico 12: Design Patterns Part 24 - Improving Our Projects with Configuration 25) Kentico 12: Design Patterns Part 25 - MVC Page Templates

Posted on by:

seangwright profile

Sean G. Wright

@seangwright

dev lead @WiredViews, founding partner @craftbrewingbiz. @Kentico MVP. love to learn / teach web dev & software engineering, collecting vinyl records, mowing my lawn, craft ๐Ÿบ

Discussion

markdown guide
 

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.