Create versatile page templates, part 2
In part 1 of the page template guides, we walked through the steps of creating a page template that displays structured product data with different styling options determined by its properties.
Next, let’s make the template truly flexible by adding the option to dynamically add Editable areas to the template based on configurable properties.
Prerequisites
This is the second part of the guide on page templates, and requires that you complete Part 1 first in order to properly follow along.
Enable widgets on the template
Let’s start by adding a single conditional editable area to the template. This will allow editors to put widgets on the page if a certain checkbox is enabled. Afterward, we can expand the functionality for more flexibility.
Add a new checkbox to the template properties
- Add a new boolean property called
UsePageBuilder
to theProductPagePageTemplateProperties
class found in the ~/Features/Products folder. - Make it default to
false
so that the template automatically uses the standard product layout. - Apply the
CheckBoxComponent
attribute to assign a check box control to the field in the template properties dialog, and set itsOrder
to 10 so it appears before the other properties.C#ProductPagePageTemplateProperties.cs... namespace TrainingGuides.Web.Features.Products; public class ProductPagePageTemplateProperties : IPageTemplateProperties { [CheckBoxComponent( Label = "Use Page Builder", ExplanationText = "Check to configure an advanced Page Builder template. Un-check to use the standard product layout.", Order = 10)] public bool UsePageBuilder { get; set; } = false; ...
Include an editable area in the template view
- Open the ProductPagePageTemplate.cs file in the same folder.
- Cut all of the code within the scope of the first
tg-component-style
tag, and paste it in a text file for later copying. - After the first
tg-component-style
tag, add anif
statement. - Check the value of the new
UsePageBuilder
property. If it is true, render an editable area with the identifierareaMain
.TheareaMain
identifier matches other templates used on this site. Using consistent identifiers makes switching templates on a page and transferring Page Builder data easier.cshtmlProductPagePageTemplate.cshtml... <tg-component-style color-scheme="@Model.Properties.ColorScheme" corner-style="@Model.Properties.CornerStyle"> @if(Model.Properties.UsePageBuilder) { <editable-area area-identifier="areaMain" /> } </tg-component-style> ...
- Add an
else
statement, and paste the code from the text file within its scope.cshtmlProductPagePageTemplate.cshtml... else { <div class="tg-padding-big"> <div class="row"> <div class="col align-self-center"> <h3>@templateModel.Name</h3> <p>@templateModel.ShortDescription</p> </div> <tg-styled-image src="@templateModel.Media.FirstOrDefault()?.FilePath" alt="@templateModel.Media.FirstOrDefault()?.Description" corner-style="@Model.Properties.CornerStyle" class="col c-product-img object-fit-cover" /> </div> <div class="row"> <div class="c-pricelist"> <div class="col"> <tg-component-style color-scheme="@ColorSchemeOption.Light1" corner-style="@Model.Properties.CornerStyle" class="c-table"> @foreach (var feature in templateModel.Features) { <div class="c-table_row"> <div class="c-table_cell"><div class="c-table_cell">@feature.Label</div></div> <div class="c-table_cell text-end"> <tg-product-feature-value feature="@feature"/> </div> </div> } </tg-component-style> </div> </div> </div> </div> } ...
@using Microsoft.AspNetCore.Html
@using System.Globalization
@using TrainingGuides.Web.Features.Products
@using TrainingGuides.Web.Features.Products.Models
@using TrainingGuides.Web.Features.Shared.OptionProviders.ColumnLayout
@using TrainingGuides.Web.Features.Shared.OptionProviders.ColorScheme
@using TrainingGuides.Web.Features.Shared.ViewComponents
@model TemplateViewModel<ProductPagePageTemplateProperties>
@{
var templateModel = Model.GetTemplateModel<ProductPageViewModel>();
}
<tg-component-style color-scheme="@Model.Properties.ColorScheme" corner-style="@Model.Properties.CornerStyle">
@if(Model.Properties.UsePageBuilder)
{
<editable-area area-identifier="areaMain" />
}
else
{
<div class="tg-padding-big">
<div class="row">
<div class="col align-self-center">
<h3>@templateModel.Name</h3>
<p>@templateModel.ShortDescription</p>
</div>
<tg-styled-image src="@templateModel.Media.FirstOrDefault()?.FilePath" alt="@templateModel.Media.FirstOrDefault()?.Description" corner-style="@Model.Properties.CornerStyle" class="col c-product-img object-fit-cover" />
</div>
<div class="row">
<div class="c-pricelist">
<div class="col">
<tg-component-style color-scheme="@ColorSchemeOption.Light1" corner-style="@Model.Properties.CornerStyle" class="c-table">
@foreach (var feature in templateModel.Features)
{
<div class="c-table_row">
<div class="c-table_cell"><div class="c-table_cell">@feature.Label</div></div>
<div class="c-table_cell text-end">
<tg-product-feature-value feature="@feature"/>
</div>
</div>
}
</tg-component-style>
</div>
</div>
</div>
</div>
}
</tg-component-style>
Check your progress
Now, if you log in to the Xperience admin and apply this template to one of the pages under Store in the Training guides pages channel, you’ll see a new checkbox in the properties.
If you tick the box and click apply, the standard product content should disappear. It will be replaced with an editable area, where you can add and drag widgets.
If you un-tick the property, you may see a warning telling you that any content in the removed editable area will be lost upon saving.
Flesh out the functionality
An editable area is fairly flexible on its own, especially if you have multiple options for sections and lots of widgets, but let’s try to make it even more versatile.
We’ll create a view component that uses template properties to determine the number of columns the template has, and how they are laid out.
Expand the template properties
- Create a new folder, ~/Features/Shared/OptionProviders/ColumnLayout.
- Add an enumeration called
ColumnLayoutOption
, with options for different layouts with one, two, and three columns.C#ColumnLayoutOption.csusing System.ComponentModel; namespace TrainingGuides.Web.Features.Shared.OptionProviders.ColumnLayout; public enum ColumnLayoutOption { [Description("One column")] OneColumn = 0, [Description("Two columns")] TwoColumnEven = 1, [Description("Two columns - Lg/Sm")] TwoColumnLgSm = 2, [Description("Two columns - Sm/Lg")] TwoColumnSmLg = 3, [Description("Three columns")] ThreeColumnEven = 4, [Description("Three columns - Sm/Lg/Sm")] ThreeColumnSmLgSm = 5, }
- Return to the ~/Features/Products/ProductPagePageTemplateProperties file and add a new string property to represent the column layout.
- Set the property to use a dropdown component, supplied by our new enumeration.
- Add a visibility condition to ensure that it only appears if the
UsePageBuilder
property’s checkbox is true (enabled).C#ProductPagePageTemplateProperties.cs... [DropDownComponent( Label = "Column layout", ExplanationText = "Select the layout of the editable areas in the template.", DataProviderType = typeof(DropdownEnumOptionProvider<ColumnLayoutOption>), Order = 40)] [VisibleIfTrue(nameof(UsePageBuilder))] public string ColumnLayout { get; set; } = nameof(ColumnLayoutOption.OneColumn); ...
Now, if you sign in to the system, you should see the new checkbox appear in the template properties of any page using this template, but only if the checkbox property to use Page Builder is enabled. At this point, the new property won’t do anything, so let’s change that.
Add supporting entities for the view component
- If the ~/Features/Shared folder does not contain a ViewComponents folder, create it.
- Add a new class called
PageBuilderColumnViewModel
, with properties for the CSS class of the column, its identifier, and anEditableAreaOptions
object.C#PageBuilderColumnViewModel.csusing Kentico.PageBuilder.Web.Mvc; namespace TrainingGuides.Web.Features.Shared.ViewComponents; public class PageBuilderColumnViewModel { public string CssClass { get; set; } = string.Empty; public string Identifier { get; set; } = string.Empty; public EditableAreaOptions? EditableAreaOptions { get; set; } }
- Add a new ViewComponent called
PageBuilderColumnsViewComponent
. - Add a
PageBuilderColumnsViewModel
class, containing a collection ofPageBuilderColumnViewModel
objects.C#PageBuilderColumnsViewModel.csnamespace TrainingGuides.Web.Features.Shared.ViewComponents; public class PageBuilderColumnsViewModel { public IEnumerable<PageBuilderColumnViewModel> PageBuilderColumns = Enumerable.Empty<PageBuilderColumnViewModel>(); }
Implement the view component logic
Now it’s time to implement the view component. Let’s give the template a limit of three columns. It’s rare to see web pages with more than three columns, and if editors need more columns than that, they can use multi-column Page Builder sections within the template columns.
Open PageBuilderColumnsViewComponent.cs and add constants to the view component, representing the identifiers used when rendering editable areas, along with bootstrap classes for managing the sizes of the columns.
Bootstrap is already included in the training guides repository.C#PageBuilderColumnsViewComponent.cs... private const string AREA_MAIN = "areaMain"; private const string AREA_SECONDARY = "areaSecondary"; private const string AREA_TERTIARY = "areaTertiary"; private const string COL_XS = "col-md-3"; private const string COL_S = "col-md-4"; private const string COL_M = "col-md-6"; private const string COL_L = "col-md-8"; private const string COL = "col-md"; ...
Use properties to access the identifier constants.
This will allow for more complicated logic in the futere. In the next guide, we will modify this view component to work with widget zones for Page Builder sections.C#PageBuilderColumnsViewComponent.cs... private string MainIdentifier { get => AREA_MAIN; set { } } private string SecondaryIdentifier { get => AREA_SECONDARY; set { } } private string TertiaryIdentifier { get => AREA_TERTIARY; set { } } ...
Add a method that takes a
ColumnLayoutOption
parameter and uses it to determine the number of columns associated with the option.C#PageBuilderColumnsViewComponent.cs... private int GetNumberOfColumns(ColumnLayoutOption columnLayoutOption) => columnLayoutOption switch { ColumnLayoutOption.TwoColumnEven or ColumnLayoutOption.TwoColumnLgSm or ColumnLayoutOption.TwoColumnSmLg => 2, ColumnLayoutOption.ThreeColumnEven or ColumnLayoutOption.ThreeColumnSmLgSm => 3, ColumnLayoutOption.OneColumn or _ => 1 }; ...
Define a method that returns a
PageBuilderColumnViewModel
based on the index of the column, the selectedColumnLayoutOption
, and anEditableAreaOptions
object.- Assign bootstrap column classes according to the sizes dictated in the option’s name.
- For layout options where the columns are not the same width, apply the main identifier if it is the largest one.C#PageBuilderColumnsViewComponent.cs
... private PageBuilderColumnViewModel GetColumn(int columnIndex, ColumnLayoutOption columnLayoutOption, EditableAreaOptions editableAreaOptions) { string cssClass = string.Empty; string columnIdentifier; switch (columnLayoutOption) { case ColumnLayoutOption.TwoColumnEven: //first column is main cssClass = COL_M; columnIdentifier = columnIndex == 0 ? MainIdentifier : SecondaryIdentifier; break; case ColumnLayoutOption.TwoColumnLgSm: //first column is main if (columnIndex == 0) { cssClass += COL_L; columnIdentifier = MainIdentifier; } else { cssClass += COL_S; columnIdentifier = SecondaryIdentifier; } break; case ColumnLayoutOption.TwoColumnSmLg: //second column is main if (columnIndex == 0) { cssClass += COL_S; columnIdentifier = SecondaryIdentifier; } else { cssClass += COL_L; columnIdentifier = MainIdentifier; } break; case ColumnLayoutOption.ThreeColumnEven: //middle column is main cssClass += COL_S; columnIdentifier = columnIndex == 1 ? MainIdentifier : columnIndex == 0 ? SecondaryIdentifier : TertiaryIdentifier; break; case ColumnLayoutOption.ThreeColumnSmLgSm: //middle column is main if (columnIndex == 1) { cssClass += COL_M; columnIdentifier = MainIdentifier; } else { cssClass += COL_XS; columnIdentifier = columnIndex == 0 ? SecondaryIdentifier : TertiaryIdentifier; } break; case ColumnLayoutOption.OneColumn: default: //sole column is main columnIdentifier = MainIdentifier; cssClass += COL; break; } return new PageBuilderColumnViewModel { CssClass = cssClass, Identifier = columnIdentifier, EditableAreaOptions = editableAreaOptions }; } ...
Implement the
Invoke
method withColumnLayoutOption
andEditableAreaOptions
parameters.- Use the
GetNumberOfColumns
method to construct afor
loop. In the loop, assemble a list ofPageBuilderColumn
view models using theGetColumn
method and the current index within the loop. - Use the list to create a
PageBuilderColumnsViewModel
and pass it to a view at the path ~/Features/Shared/ViewComponents/PageBuilderColumns.cshtml.ThePageBuilderColumns
view doesn’t exist yet, but don’t worry. We’ll add it in the next section.C#PageBuilderColumnsViewComponent.cs... public IViewComponentResult Invoke(ColumnLayoutOption columnLayoutOption, EditableAreaOptions editableAreaOptions) { int numberOfColumns = GetNumberOfColumns(columnLayoutOption); var columns = new List<PageBuilderColumnViewModel>(); for (int index = 0; index < numberOfColumns; index++) { columns.Add(GetColumn(index, columnLayoutOption, editableAreaOptions)); } var model = new PageBuilderColumnsViewModel { PageBuilderColumns = columns, }; return View("~/Features/Shared/ViewComponents/PageBuilderColumns.cshtml", model); } ...
- Use the
using Kentico.PageBuilder.Web.Mvc;
using Microsoft.AspNetCore.Mvc;
using TrainingGuides.Web.Features.Shared.OptionProviders.ColumnLayout;
namespace TrainingGuides.Web.Features.Shared.ViewComponents;
public class PageBuilderColumnsViewComponent : ViewComponent
{
private const string AREA_MAIN = "areaMain";
private const string AREA_SECONDARY = "areaSecondary";
private const string AREA_TERTIARY = "areaTertiary";
private const string COL_XS = "col-md-3";
private const string COL_S = "col-md-4";
private const string COL_M = "col-md-6";
private const string COL_L = "col-md-8";
private const string COL = "col-md";
private string MainIdentifier
{
get => AREA_MAIN;
set { }
}
private string SecondaryIdentifier
{
get => AREA_SECONDARY;
set { }
}
private string TertiaryIdentifier
{
get => AREA_TERTIARY;
set { }
}
public IViewComponentResult Invoke(ColumnLayoutOption columnLayoutOption,
EditableAreaOptions editableAreaOptions)
{
int numberOfColumns = GetNumberOfColumns(columnLayoutOption);
var columns = new List<PageBuilderColumnViewModel>();
for (int index = 0; index < numberOfColumns; index++)
{
columns.Add(GetColumn(index, columnLayoutOption, editableAreaOptions));
}
var model = new PageBuilderColumnsViewModel
{
PageBuilderColumns = columns,
};
return View("~/Features/Shared/ViewComponents/PageBuilderColumns.cshtml", model);
}
private int GetNumberOfColumns(ColumnLayoutOption columnLayoutOption) => columnLayoutOption switch
{
ColumnLayoutOption.TwoColumnEven or
ColumnLayoutOption.TwoColumnLgSm or
ColumnLayoutOption.TwoColumnSmLg
=> 2,
ColumnLayoutOption.ThreeColumnEven or
ColumnLayoutOption.ThreeColumnSmLgSm
=> 3,
ColumnLayoutOption.OneColumn or
_
=> 1
};
private PageBuilderColumnViewModel GetColumn(int columnIndex, ColumnLayoutOption columnLayoutOption, EditableAreaOptions editableAreaOptions)
{
string cssClass = string.Empty;
string columnIdentifier;
switch (columnLayoutOption)
{
case ColumnLayoutOption.TwoColumnEven:
//first column is main
cssClass = COL_M;
columnIdentifier = columnIndex == 0 ? MainIdentifier : SecondaryIdentifier;
break;
case ColumnLayoutOption.TwoColumnLgSm:
//first column is main
if (columnIndex == 0)
{
cssClass += COL_L;
columnIdentifier = MainIdentifier;
}
else
{
cssClass += COL_S;
columnIdentifier = SecondaryIdentifier;
}
break;
case ColumnLayoutOption.TwoColumnSmLg:
//second column is main
if (columnIndex == 0)
{
cssClass += COL_S;
columnIdentifier = SecondaryIdentifier;
}
else
{
cssClass += COL_L;
columnIdentifier = MainIdentifier;
}
break;
case ColumnLayoutOption.ThreeColumnEven:
//middle column is main
cssClass += COL_S;
columnIdentifier = columnIndex == 1
? MainIdentifier
: columnIndex == 0 ?
SecondaryIdentifier : TertiaryIdentifier;
break;
case ColumnLayoutOption.ThreeColumnSmLgSm:
//middle column is main
if (columnIndex == 1)
{
cssClass += COL_M;
columnIdentifier = MainIdentifier;
}
else
{
cssClass += COL_XS;
columnIdentifier = columnIndex == 0 ?
SecondaryIdentifier : TertiaryIdentifier;
}
break;
case ColumnLayoutOption.OneColumn:
default:
//sole column is main
columnIdentifier = MainIdentifier;
cssClass += COL;
break;
}
return new PageBuilderColumnViewModel
{
CssClass = cssClass,
Identifier = columnIdentifier,
EditableAreaOptions = editableAreaOptions
};
}
}
Create the component view
- Add a new file named PageBuilderColumns.cshtml to the ~/Features/Shared/ViewComponents folder.
- Set the
model
toPageBuilderColumnsViewModel
, and nestdiv
elements with thecontainer
androw
classes.cshtmlPageBuilderColumns.cshtml@using TrainingGuides.Web.Features.Shared.ViewComponents @model PageBuilderColumnsViewModel <div class="container tg-pb-col"> <div class="row"> </div> </div>
- Use a
foreach
loop to iterate through the model’sPageBuilderColumns
collection. - Within the loop, add a
div
styled with the column’scssClass
property.cshtmlPageBuilderColumns.cshtml... @foreach (var column in Model.PageBuilderColumns) { <div class="@column.CssClass"> </div> } ...
- Render an editable area with the
editable-area
tag helper from Xperience.- Set the
area-options
attribute to the column’sEditableAreaOptions
property.cshtmlPageBuilderColumns.cshtml... <editable-area area-identifier="@column.Identifier" area-options="column.EditableAreaOptions" /> ...
- Set the
Now, the view should look like this:
@using TrainingGuides.Web.Features.Shared.ViewComponents
@model PageBuilderColumnsViewModel
<div class="container tg-pb-col">
<div class="row">
@foreach (var column in Model.PageBuilderColumns)
{
<div class="@column.CssClass">
<editable-area area-identifier="@column.Identifier" area-options="column.EditableAreaOptions" />
</div>
}
</div>
</div>
Use the view component in the template
Now the view component is finished. Let’s add it to the template view so we can test our progress.
- Return to the ~/Features/Shared/Products/ProductPagePageTemplate.cshtml file.
- Remove the
editable-area
tag from the view, and replace it with the new view component.cshtmlProductPagePageTemplate.cshtml... @if(Model.Properties.UsePageBuilder) { <vc:page-builder-columns column-layout-option="@columnLayout" editable-area-options="@new EditableAreaOptions()" /> } ...
- In the razor code block that sets the
templateModel
variable, default the column layout to one column if the model’sColumnLayout
property can’t be parsed.cshtmlProductPagePageTemplate.cshtml@{ var templateModel = Model.GetTemplateModel<ProductPageViewModel>(); if (!Enum.TryParse(Model.Properties.ColumnLayout, out ColumnLayoutOption columnLayout)) { columnLayout = ColumnLayoutOption.OneColumn; } }
@using Microsoft.AspNetCore.Html
@using System.Globalization
@using TrainingGuides.Web.Features.Products
@using TrainingGuides.Web.Features.Products.Models
@using TrainingGuides.Web.Features.Shared.OptionProviders.ColumnLayout
@using TrainingGuides.Web.Features.Shared.OptionProviders.ColorScheme
@using TrainingGuides.Web.Features.Shared.ViewComponents
@model TemplateViewModel<ProductPagePageTemplateProperties>
@{
var templateModel = Model.GetTemplateModel<ProductPageViewModel>();
if (!Enum.TryParse(Model.Properties.ColumnLayout, out ColumnLayoutOption columnLayout))
{
columnLayout = ColumnLayoutOption.OneColumn;
}
}
<tg-component-style color-scheme="@Model.Properties.ColorScheme" corner-style="@Model.Properties.CornerStyle">
@if(Model.Properties.UsePageBuilder)
{
<vc:page-builder-columns column-layout-option="@columnLayout" editable-area-options="@new EditableAreaOptions()" />
}
else
{
<div class="tg-padding-big">
<div class="row">
<div class="col align-self-center">
<h3>@templateModel.Name</h3>
<p>@templateModel.ShortDescription</p>
</div>
<tg-styled-image src="@templateModel.Media.FirstOrDefault()?.FilePath" alt="@templateModel.Media.FirstOrDefault()?.Description" corner-style="@Model.Properties.CornerStyle" class="col c-product-img object-fit-cover" />
</div>
<div class="row">
<div class="c-pricelist">
<div class="col">
<tg-component-style color-scheme="@ColorSchemeOption.Light1" corner-style="@Model.Properties.CornerStyle" class="c-table">
@foreach (var feature in templateModel.Features)
{
<div class="c-table_row">
<div class="c-table_cell"><div class="c-table_cell">@feature.Label</div></div>
<div class="c-table_cell text-end">
<tg-product-feature-value feature="@feature"/>
</div>
</div>
}
</tg-component-style>
</div>
</div>
</div>
</div>
}
</tg-component-style>
Apply the same concept to a more general page template
Looking over the page template we’ve just created, you may find it a bit of a waste to have all these options for Page Builder layouts and appearance available only for product pages.
Since most of what we’ve made is reusable, it will be straightforward to create a more general-purpose page template that has these Page Builder features, and can be used on any page type.
- Create a new ~/Features/Shared/Templates folder.
- Add a new page template properties file called GeneralPageTemplateProperties.cs.
- Include the same properties as ProductPagePageTemplateProperties.cs, except for the checkbox. This template will always use Page Builder areas.C#GeneralPageTemplateProperties.cs
using Kentico.PageBuilder.Web.Mvc.PageTemplates; using Kentico.Xperience.Admin.Base.FormAnnotations; using TrainingGuides.Web.Features.Shared.OptionProviders; using TrainingGuides.Web.Features.Shared.OptionProviders.ColumnLayout; using TrainingGuides.Web.Features.Shared.OptionProviders.CornerStyle; using TrainingGuides.Web.Features.Shared.OptionProviders.ColorScheme; namespace TrainingGuides.Web.Features.Shared; public class GeneralPageTemplateProperties : IPageTemplateProperties { [DropDownComponent( Label = "Color scheme", ExplanationText = "Select the color scheme of the template.", DataProviderType = typeof(DropdownEnumOptionProvider<ColorSchemeOption>), Order = 10)] public string ColorScheme { get; set; } = nameof(ColorSchemeOption.TransparentDark); [DropDownComponent( Label = "Corner type", ExplanationText = "Select the corner type of the template.", DataProviderType = typeof(DropdownEnumOptionProvider<CornerStyleOption>), Order = 20)] public string CornerStyle { get; set; } = nameof(CornerStyleOption.Round); [DropDownComponent( Label = "Column layout", ExplanationText = "Select the layout of the editable areas in the template.", DataProviderType = typeof(DropdownEnumOptionProvider<ColumnLayoutOption>), Order = 30)] public string ColumnLayout { get; set; } = nameof(ColumnLayoutOption.OneColumn); }
- Add a view called GeneralPageTemplate.cshtml, copying the parts from ProductPagePageTemplate.cshtml that are not specifically related to products.cshtmlGeneralPageTemplate.cshtml
@using Microsoft.AspNetCore.Html @using TrainingGuides.Web.Features.Shared @using TrainingGuides.Web.Features.Shared.OptionProviders.ColumnLayout @using TrainingGuides.Web.Features.Shared.ViewComponents @model TemplateViewModel<GeneralPageTemplateProperties> @{ if (!Enum.TryParse(Model.Properties.ColumnLayout, out ColumnLayoutOption columnLayout)) { columnLayout = ColumnLayoutOption.OneColumn; } } <tg-component-style color-scheme="@Model.Properties.ColorScheme" corner-style="@Model.Properties.CornerStyle"> <vc:page-builder-columns column-layout-option="@columnLayout" editable-area-options="@new EditableAreaOptions { DefaultSectionIdentifier = ComponentIdentifiers.Sections.GENERAL }" /> </tg-component-style>
- Register the template in a file called GeneralPageTemplate.cs.
- Do not include the
ContentTypeNames
property.OmittingContentTypeNames
allows the template to be used for any page content type.C#GeneralPageTemplate.csusing Kentico.PageBuilder.Web.Mvc.PageTemplates; using TrainingGuides.Web.Features.Shared; [assembly: RegisterPageTemplate( identifier: GeneralPageTemplate.IDENTIFIER, name: "General page template", propertiesType: typeof(GeneralPageTemplateProperties), customViewName: "~/Features/Shared/Templates/GeneralPageTemplate.cshtml", IconClass = "xp-square")] namespace TrainingGuides.Web.Features.Shared; public static class GeneralPageTemplate { public const string IDENTIFIER = "TrainingGuides.GeneralPageTemplate"; }
Now, if you log in to the administration interface, you should have a general-use page template with properties to determine its color, corner styling, and editable area layout.
What’s next?
The next guide in this series will re-use the view component and tag helpers we’ve created in this guide to make flexible Page Builder sections. Then, in the following guides, we will create widgets for simple call-to-action (CTA) buttons and products.
Take another look at the first guide in this series to overview how Page Builder applies to business scenarios and review the mockups of the page functionality this series aims to achieve.
Apply your knowledge
If you’d like to put your knowledge to the test, try swapping out the UsePageBuilder
checkbox in ProductPagePageTemplateProperties
for another enumeration-based dropdown.
Create a third option which keeps the structured product data, while rendering editable areas at the top and bottom of the page.