EPiServer HTML helper for on-page edit functionality

Introduction

Let’s say we want to make page heading editable.

View:

@model PageViewModel<ArticlePage>
<h1 @Html.EditAttributes(x => x.CurrentPage.Heading)>@Model.CurrentPage.Heading</h1>

This code will enable on-page edit functionality in edit mode:

EPiServer on-page edit functioanlity

And render empty H1 tag in view mode if page heading is missing:

<h1></h1>

One way to solve this is to use PageEditing.PageIsInEditMode to check if the page is opened in edit mode:

@if (PageEditing.PageIsInEditMode)
{
    <h1 @Html.EditAttributes(x => x.CurrentPage.Heading)>@Model.CurrentPage.Heading</h1>
}
@if (!PageEditing.PageIsInEditMode && !string.IsNullOrEmpty(Model.CurrentPage.Heading))
{
    <h1>@Model.CurrentPage.Heading</h1>
}

Another way would be to use custom helper:

@Html.EditableField(x => x.CurrentPage.Heading, "h1")

Custom helper

public static class EpiHtmlHelpers
{
    public static MvcHtmlString EditableField<TModel, TResult>(this HtmlHelper<TModel> model,
                                                                Expression<Func<TModel, TResult>> expression,
                                                                string tagName)
    {
        return EditableField(model, expression, tagName, null);
    }

    public static MvcHtmlString EditableField<TModel, TResult>(this HtmlHelper<TModel> model,
                                                                Expression<Func<TModel, TResult>> expression,
                                                                string tagName,
                                                                object htmlAttributes)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, model.ViewData);
        var hasValue = metadata.Model != null;

        // edit mode (with or without value)
        if (PageEditing.PageIsInEditMode)
        {
            return RenderPropertyForEditMode<TModel, TResult>(tagName, htmlAttributes, metadata);
        }
        // view mode with value
        if (hasValue)
        {
            return RenderPropertyForViewMode<TModel, TResult>(tagName, htmlAttributes, metadata);
        }
        // view mode without value
        return MvcHtmlString.Empty;
    }

    private static MvcHtmlString RenderPropertyForEditMode<TModel, TResult>(string tagName,
                                                                            object htmlAttributes,
                                                                            ModelMetadata metadata)
    {
        var tagBuilder = new TagBuilder(tagName);

        tagBuilder.MergeAttribute("data-epi-property-name", metadata.PropertyName);
        tagBuilder.MergeAttribute("data-epi-use-mvc", "True");

        if (htmlAttributes != null)
        {
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            tagBuilder.MergeAttributes(attributes);
        }

        if (metadata.Model != null)
        {
            tagBuilder.InnerHtml = metadata.Model.ToString();
        }

        return MvcHtmlString.Create(tagBuilder.ToString());
    }

    private static MvcHtmlString RenderPropertyForViewMode<TModel, TResult>(string tagName,
                                                                            object htmlAttributes,
                                                                            ModelMetadata metadata)
    {
        var tagBuilder = new TagBuilder(tagName);

        if (htmlAttributes != null)
        {
            var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            tagBuilder.MergeAttributes(attributes);
        }

        tagBuilder.InnerHtml = metadata.Model.ToString();

        return MvcHtmlString.Create(tagBuilder.ToString());
    }
}

You need to include the following namespaces:

using System;
using System.Linq.Expressions;
using System.Web.Mvc;
using EPiServer.Editor;

Usage

Code:

@Html.EditableField(x => x.CurrentPage.Heading, "h1")

View mode:

<h1>This is heading</h1>

Edit mode:

<h1 data-epi-property-name="Heading" data-epi-use-mvc="True">This is heading</h1>

 

Code:

@Html.EditableField(x => x.CurrentPage.Heading, "h1", new { @class = "myClass" })

View mode:

<h1 class="myClass">This is heading</h1>

Edit mode:

<h1 class="myClass" data-epi-property-name="Heading" data-epi-use-mvc="True">This is heading</h1>
comments powered by Disqus