EPiServer  /  CMS May 21, 2013

EPiServer editing delight challenge
– MVC solution

NOTE This article presents a solution to a problem/challenged described in an earlier article. It's intended to be read in context of first having read the earlier article.

Solving a small but tricky problem related to customised rendering of a property in EPiServer 7 with ASP.NET MVC.

Starting with the simple stuff we render each of the properties using the PropertyFor method.

<h1>@Html.PropertyFor(x => x.Heading)</h1>
<p>@Html.PropertyFor(x => x.Intro)</p>

Then we make both properties editable using a text area by adding a UI hint to each of them.

//using EPiServer.Web;

[UIHint(UIHint.Textarea)]
public virtual string Heading { get; set; }

[UIHint(UIHint.Textarea)]
public virtual string Intro { get; set; }

Customise the rendering

Moving on to the next requirement we create an extension method for strings that replaces line breaks with br tags. We make it return an IHtmlString so that the br tags won't be encoded.

public static IHtmlString ToLineBreakString(this string original)
{
    var parsed = string.Empty;
    if (!string.IsNullOrEmpty(original))
    {
        parsed = HttpUtility.HtmlEncode(original);
        parsed = parsed.Replace("\n", "<br />");
    }

    return new MvcHtmlString(parsed);
}

Now, how do we use this method? We could modify the view to render the property's value directly and add edit attributes to the h1 tag, like this:

<h1 @Html.EditAttributes(x => x.Heading)>
    @Model.Heading.ToLineBreakString()
</h1>

At first it may seem like we've solved the challenge. Both properties are editable in on-page-edit mode using text areas and a line break in the Heading property is replaced with a br tag. However, let's look at what happens when we make a change to the Heading property:

The line break in the Heading property isn't replaced with a br when the new value is inserted into the page during editing. We're not meeting the requirement that both properties should offer a realistic preview at all times.

This happens because the CMS has no way of knowing that we're customizing the rendering of the property in our view. Instead it updates the DOM node that represents the property with what would be rendered by the PropertyFor method.

Fixing the preview

If you've read my article about how PropertyFor works you know that means it will replace it with what the DisplayFor method would return, meaning the default display template for strings.

To fix this we could create a display template named string or Textarea (matching the UI hint) and render the value with the help of our extension method there. Given that we update the view to again render the Heading property using either PropertyFor or DisplayFor we would then have fixed the preview issue.

That would however mean that a line break in the Intro property would also be replaced with a br tag, violating the requirement that it should not be.

What to do then? Seems like the requirements are conflicting. Perhaps all is lost and it's time to give up?

Not quite. We just need to sprinkle our code with some magic dust made exactly according to the recipe.

First we create a display template named LineBreaked that renders a string using our extension method.

@model string
@Model.ToLineBreakString()

Next we add another UI hint to the Heading property, this time with the name of the display template.

[UIHint("LineBreaked")]
[UIHint(UIHint.Textarea)]
public virtual string Heading { get; set; }

That alone doesn't yield any effect as we now have two UI hints and, at least for me, the Textarea one will be used.

The final step that makes it all work is to modify the second UI hint so that it won't be used by PropertyFor or DisplayFor, only by the CMS when locating the approriate editor for the property. To do so we use an overload of the UIHintAttribute class' constructor and pass it a string representing a "presentation layer". Specifically the string exposed by the Edit constant in EPiServer's PresentationLayer class.

[UIHint("LineBreaked")]
[UIHint(UIHint.Textarea, PresentationLayer.Edit)]
public virtual string Heading { get; set; }

And with that the challenge is solved.

Alternative solution

The above solution works and will effect all of the places where the Heading property is rendered. There is however also an alternative, simpler, solution that will only effect the rendering in a specific place.

Instead of adding the additional UI hint and specifying presentation layer for the Textarea UI hint we can render the property using PropertyFor and specify a tag matching the display templates name.

<h1>@Html.PropertyFor(x => x.Heading, new { tag = "LineBreaked"})</h1>

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

My book

Want a structured way to learn EPiServer 7 development? Check out my book on Leanpub!

More about EPiServer CMS