Meta Properties for On-Page Editing

While on-page editing within Optimizely + Episerver is a powerful tool, by default it continues to hide some important metadata. Blend's Director of Development Bob Davidson highlights how to programmatically surface meta properties within on-page editing in this Coding with Bob video.

6/8/2021

Authored by

Categorized

  • Development
  • Optimizely

On-page-editing is, in my opinion, one Episerver's more distinctive features. This is a bit of a controversial opinion. A common complaint is that on-page-editing doesn't allow easy access to non-visual elements, such as page metadata and settings. If one has to switch to all-properties to edit those, why bother switching back to on-page-editing view?

It's a valid concern, but there is a relatively simple way to make at least some of these otherwise hidden attributes available from on-page-editing. By building a simple block type and adding some support to the UI, we can create a toolbar of "Property Blocks," each of which can clicked to show a modal of properties without leaving on-page-editing.

example2.gif

The process is a little involved, but this video walks through all the steps:

 

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.

Blend's Director of Development, Bob Davidson, provides tutorials on all things development.

His web series, Coding with Bob, can be found on Youtube. Check it out!

Across the web.

Here's somewhere else you might find Bob beyond Blend.

Coding with Bob

Coding with Bob is a YouTube series by Blend's Bob Davidson: a little .NET coding, a little Optimizely development work, and a few things related to both.