Glass Mapper and line breaks in multi-line fields in Sitecore

Glass Mapper and line breaks in multi-line fields in Sitecore

If you are using Glass.Mapper and have multi-line fields in Sitecore that contains line breaks (which is sort of the reason for the multi-line field) you might have run into this “issue” (or at least unexpected behavior in my eyes) that Glass.Mapper doesn’t convert line breaks (\r\n) to <br> tags, although Sitecore outputs <br> tags if using the standard API.

Their reasoning is something along the lines of not wanting to manipulate the raw data as it might not always be used in an HTML-context where <br> tags would be the right choice. When rendering a multi-line field in HTML you need line breaks to be <br> tags, but if you use it in non-HTML contexts the <br> tags would be bad, so there are both pros and cons to their approach, but I would probably prefer it if it would default to <br> and an option to change it.

This was specifically a problem for our editors, who would insert line breaks through the Experience Editor and then they would not show up when publishing the page – leaving them confused.

The issue(s)

There are actually two issues going on here – one in normal mode and one in edit mode (Experience Editor).

When Glass Mapper outputs the content of fields it does it differently depending on the page mode. When not in edit mode it just outputs the raw value for most fields except Link and Image types while in edit mode it runs the renderField pipeline (more about this at the bottom).

One solution to this issue is to create a custom data mappe for Glass, that extends the default mapper for string. (solution show further down)

Solution – Custom data mapper

I started to look into how Glass.Mapper handles rendering the different field types in the different situations. With a little help from Mike Edwards and the guys in the #glass channel on the Sitecore Slack channel, who pointed me in the right direction, I ended up making a custom data mapper – MultiLineSitecoreFieldStringMapper (extending the default SitecoreFieldStringMapper).

using System;
using System.Collections.Concurrent;
using Glass.Mapper.Sc;
using Glass.Mapper.Sc.Configuration;
using Glass.Mapper.Sc.DataMappers;
using Sitecore.Data.Fields;

namespace YourNamespace
{
    public class MultiLineSitecoreFieldStringMapper : SitecoreFieldStringMapper
    {
        private static readonly ConcurrentDictionary<Guid, bool> IsMultiLineDictionary = new ConcurrentDictionary<Guid, bool>();

        public override object GetField(Field field, SitecoreFieldConfiguration config,
            SitecoreDataMappingContext context)
        {
            var value = (string) base.GetField(field, config, context);

            var guid = field.ID.Guid;
            if (IsMultiLineDictionary.ContainsKey(guid) && IsMultiLineDictionary[guid])
                return FixMultiLineFieldLineBreaks(value);

            var isMultiLine = field.TypeKey == "multi-line text";
            IsMultiLineDictionary.TryAdd(guid, isMultiLine);

            if (!isMultiLine)
                return value;

            return FixMultiLineFieldLineBreaks(value);
        }

        private static string FixMultiLineFieldLineBreaks(string value)
        {
            return value?.Replace("\r\n", "<br>");
        }
    }
}

It extends the default functionality from the default SitecoreFieldStringMapper and basically just replaces the \r\n with <br> if the field being rendered is a multi-line field.

To make Glass.Mapper use our custom data mapper we have to add it to the resolver’s DataMapperFactory. This is done in the GlassMapperScCustom class that is generated when you install the NuGet package. We add it first so that it is used instead of the existing mapper for string., like shown below (you could probably just remove the existing one instead as well).

public static  class GlassMapperScCustom
{
    public static IDependencyResolver CreateResolver(){
        var config = new Glass.Mapper.Sc.Config();
        var resolver = new DependencyResolver(config);
        resolver.DataMapperFactory.Insert(0, () => new MultiLineSitecoreFieldStringMapper());
        return resolver;
    }

    /* ... */
}

Multi-line fields with line breaks will now be rendered “as expected” – with line breaks.

Note about the Experience Editor

There is a bug in recent versions of Sitecore – at least 8.2 and some versions of 8.1 – where the renderField pipeline doesn’t output line breaks as <br> by default. This means you can add line breaks to your multi-line field and they will be saved, but when refreshing the page they will appear to be gone.

When in edit mode Glass Mapper runs the renderField pipeline to get the value of a field, so our custom data mapper is not used in this situation.

This is the same way Sitecore does it if you use the default API, like this:

@Html.Sitecore().Field("Multi line field")

Sitecore is tracking this bug by reference number 95002 and you can read a bit more about it and how to fix it in my other post.