Video summary steps.
A summary of the steps, and some code snippets follow:
1. Create the base block type, which will be used as a marker for rendering purposes:
public abstract class PropertyBlock : BlockData
{
}
2. Create a bundle of metadata properties as a PropertyBlock
:
[ContentType(GUID = "20b74707-145f-4523-be9d-2520ea2221b5",
DisplayName = "Page Meta Data",
AvailableInEditMode = false)] // It's important this is *not* available in edit mode
public class PageMetaDataPropertyBlock : PropertyBlock
{
[Display(
GroupName = Global.GroupNames.MetaData,
Order = 100)]
[CultureSpecific]
public virtual string MetaTitle
{
get; set;
}
[Display(
GroupName = Global.GroupNames.MetaData,
Order = 200)]
[CultureSpecific]
[BackingType(typeof(PropertyStringList))]
public virtual string[] MetaKeywords { get; set; }
[Display(
GroupName = Global.GroupNames.MetaData,
Order = 300)]
[CultureSpecific]
[UIHint(UIHint.Textarea)]
public virtual string MetaDescription { get; set; }
}
3. Create a controller to handle rendering all property blocks:
[TemplateDescriptor(Inherited = true)]
public class PropertyBlockController : BlockController<PropertyBlock>
{
public override ActionResult Index(PropertyBlock currentContent)
{
var typeName = currentContent.GetOriginalType().Name;
// Adjust the view paths for what makes sense for your team's standards
return PartialView($"~/Views/PropertyBlocks/{typeName}.cshtml", currentContent);
}
}
4. Add the property block as a property to your page model:
[Display(
Name = "Page Meta Data",
GroupName = Global.GroupNames.MetaData,
Order = 300)]
public virtual PageMetaDataPropertyBlock PageMetaData { get; set; }
Note: Because this is a local block object, you cannot set [CultureSpecific]
on the PageMetaData
property, but rather on the properties of the PageMetaDataPropertyBlock
.
5. Reference the new properties in the view. For example:
<title>@Model.CurrentPage.PageMetaData.MetaTitle</title>
@if (Model.CurrentPage.PageMetaData.MetaKeywords != null && Model.CurrentPage.PageMetaData.MetaKeywords.Length > 0)
{
<meta name="keywords" content="@string.Join(",", Model.CurrentPage.PageMetaData.MetaKeywords)" />
}
@if (!string.IsNullOrWhiteSpace(Model.CurrentPage.PageMetaData.MetaDescription))
{
<meta name="description" content="@Model.CurrentPage.PageMetaData.MetaDescription" />
}
6. Create space for the Property Block buttons in edit mode. In your template, add something like:
@if (EPiServer.Editor.PageEditing.PageIsInEditMode)
{
<div class="edit-tool-bar">
<div style="display: inline-block; width: 200px">
@Html.PropertyFor(x => x.CurrentPage.PageMetaData)
</div>
@RenderSection("editbar", false)
</div>
}
7. The @RenderSection("editbar", false)
gives you a section in which you can add page-specific property blocks. For example:
@if (EPiServer.Editor.PageEditing.PageIsInEditMode)
{
@section editbar {
<div style="display: inline-block; width: 200px">
@Html.PropertyFor(x => x.CurrentPage.ProductPageSettings)
</div>
}
}
8. If you have nested templates, in the outer templates, you'll need to "punch a whole" to pass through the editbar
section. For example, in Alloy, in the _TwoPlusOne.cshtml
file, you would need:
@if (EPiServer.Editor.PageEditing.PageIsInEditMode)
{
@section editbar {
@RenderSection("editbar", false)
}
}
9. Create a view for each Property Block to render the "button" in the editbar
space. For example:
@* ~/Views/PropertyBlocks/PageMetaDataPropertyBlock.cshtml *@
@model PageMetaDataPropertyBlock
@{
var hasPageTitle = !string.IsNullOrEmpty(Model.MetaTitle);
}
<b>PAGE META DATA @(hasPageTitle ? "" : "[WARNING]")</b>
You can use this technique for other items as well. For example, I've done some curated navigation as a property block that renders the navigation (and is not in the editbar
section, but rather where the navigation needs to render). Footers often work well this way as well.
On-page-editing is often neglected, but with this and similar techniques, there is quite a bit we can do to make the experience better and more usable--all without writing a single line of Dojo.