Render wrapper around components in Experience Editor (aka Page Editor)

Context

When you working in Experience Editor (previously Page Editor) it is quite difficult to understand hierarchy of controls and their relation one to each other, moreover it is not always clear where your control ends and placeholder starts.
Why not to add some wrappers around controls? Lets see how we can do this.

Approach

We need to create pipeline processor that will inject some markup around component if page is in editing mode.

render Rendering processor

Of cause you could add some conditional elements directly into your views, but in this case it would be difficult to maintain and keep them consistent when the project will grow.

First of all we need to understand how Sitecore renders controls. As usual we need to look through pipelines – one that we need is mvc.renderRendering which you can find in Sitecore.Mvc.config. We should also take a look at Sitecore.MvcExperienceEditor.config as it adds AddWrapper processors (what a nice coincident =) to the pipeline we are interested in. The main goal of this processor is to add ChromeData to each control, which will be used by Experience Editor scripts.

AddWrapper

AddWrapper does the following:

  1. Check PageMode – this is only extension for Editor, therefore it return null in all other cases.
  2. Create required Marker (class that implements IMarker interface and return start and end tags for injection)
  3. Create instance of disposable class Wrapper and add it to pipeline arguments (args.Disposables) – this is the most tricky part.

When Wrapper constructor called it writes start tag from Marker to rendering output stream. End tag should be added after generation of controls markup – it happens when pipeline is finished and method Dispose called on arguments object. The method iterated over args.Disposables and calls Wrapper Dispose, which renders end tag.

IMPLEMENTATION

Bearing in mind everything above we need to create following processor:

public class AddColumnWrapper : ExecuteRenderer
{
    public override void Process(RenderRenderingArgs args)
    {
        if (args.Rendered || Context.Site == null || !Context.PageMode.IsPageEditorEditing || args.Rendering.RenderingType == "Layout")
        {
            return;
        }

        var marker = this.GetMarker(args);
        if (marker == null)
        {
            return;
        }

        var index = args.Disposables.FindIndex(x => x.GetType() == typeof(Wrapper));
        if (index < 0) index = 0;
        args.Disposables.Insert(index, new Wrapper(args.Writer, marker));
    }

    protected virtual IMarker GetMarker(RenderRenderingArgs args)
    {
        // logic to create your Marker goes here
    }
}

It would check required condition, create marker and wrapper and add them to arguments. Important here (selected) is that marker should be added before other markers to generate closing tag in correct order (last-in-first-out)

bugs

And here we come to a bug in Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper processor, which just add wrapper to the end of a Disposables (which results in first-in-first-out rendering).

...
args.Disposables.Add((IDisposable) new Wrapper(args.Writer, marker));
...

Unfortunately, you won’t be able to find this bug until you tried to add custom wrapper, which I did.

Configuration

<pipelines>
  <mvc.renderRendering>
    <processor patch:before="processor[@type='Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper, Sitecore.Mvc.ExperienceEditor']"
                type="Example.Pipelines.Response.RenderRendering.AddColumnWrapper, Example" />
    <processor patch:after="processor[@type='Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper, Sitecore.Mvc.ExperienceEditor']"
                type="Example.Pipelines.Response.RenderRendering.AddComponentWrapper, Example" />
    <processor patch:instead="processor[@type='Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper, Sitecore.Mvc.ExperienceEditor']"
                type="Example.Pipelines.Response.RenderRendering.AddWrapperFix, Example" />
  </mvc.renderRendering>
</pipelines>

In my case there were 2 wrappers: one to change size of control (according grid columns) and second to add markup, while selected lines represent fix for Experience editor library.

Results

Wrappers rendered around components in Experience editor

Finally, that’s how my components looks now.

Follow me on twitter @true_shoorik. Share if the post was useful for you.

Render wrapper around components in Experience Editor (aka Page Editor)

4 thoughts on “Render wrapper around components in Experience Editor (aka Page Editor)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s