I have been going through the book Professional Sitecore 8 Development by Phil Wicklund and Jason Wilkerson. The book has been very helpful to me but I've got an issue I haven't been able to figure out. In Chapter 11, I've followed the section on Customizing Rendering in the Experience Editor. The code given in the book works, in that I can see the new custom renderings. However Sitecore's JavaScript validation has been throwing XHTML errors for each of my placeholders. After researching and debugging for a while, I found that this is because the GetEnd() method that contains the closing </div>
tags executes between the closing <code></code>
blocks for the rendering and the placeholder.
Here is my main layout:
@using Sitecore.Mvc
<!DOCTYPE html>
<html lang="@Sitecore.Context.Language.CultureInfo.TwoLetterISOLanguageName">
<head>
<title></title>
<link rel="stylesheet" href="/Content/foundation.sitecoreextensions.css"/>
</head>
<body class="@(Sitecore.Context.PageMode.IsExperienceEditor ? "edit-mode" : string.Empty)">
<header>
@Html.Sitecore().Placeholder("header")
</header>
<main>
@Html.Sitecore().Placeholder("main")
</main>
<footer>
@Html.Sitecore().Placeholder("footer")
</footer>
</body>
</html>
And here is where the custom rendering HTML is added:
using Sitecore.Mvc.ExperienceEditor.Presentation;
namespace Foundation.SitecoreExtensions.RenderingWrapper.Markers
{
public class EditorComponentRenderingMarker : IMarker
{
private readonly string _componentName;
public EditorComponentRenderingMarker(string componentName)
{
_componentName = componentName;
}
public string GetStart()
{
const string formatString = "<div class=\"component-wrapper {0}\"><span class=\"wrapper-header\">{1}</span><div class=\"component-content clearfix\">";
return string.Format(formatString, _componentName.Replace(" ", string.Empty), _componentName);
}
public string GetEnd()
{
return "</div></div>";
}
}
}
And last of all, this is what the rendered HTML looks like, as seen from Developer Tools in FireFox: (just the header component, for brevity)
<header>
<code type="text/sitecore" chrometype="placeholder" kind="open" id="header" key="header" class="scpm" data-selectable="true">
<!-- JSON that Sitecore injected --> </code>
<code type="text/sitecore" chrometype="rendering" kind="open" hintname="Header" id="r_76115221D7FF46D7AAB859A209E1C829" class="scpm" data-selectable="true">
<!-- JSON that Sitecore injected --> </code>
<div class="component-wrapper Header scEnabledChrome" sc-part-of="placeholder rendering">
<span class="wrapper-header">Header</span>
<div class="component-content clearfix">
<div>
<!-- My Rendering -->
</div>
<code type="text/sitecore" id="scEnclosingTag_r_" chrometype="rendering" kind="close" hintkey="Header" class="scpm"></code>
</div> <!-- this div needs to come before the preceding code block -->
</div> <!-- this div as well -->
<code type="text/sitecore" id="scEnclosingTag_" chrometype="placeholder" kind="close" hintname="header" class="scpm"></code>
</header>
As you can see here, the closing </div>
blocks are added between the final two <code>
blocks.
I believe GetEnd() is behaving exactly as it should in that it executes right after the component is rendered, so it makes sense that it comes right after the <code>
block that marks the end of the rendering. However, I don't understand why Sitecore's JavaScript validation throws an error for this. When I modify the HTML from developer tools and move the closing rendering <code>
block just after the two closing </div>
tags, the error no longer occurs.
I'm not sure how to fix this error. Is it an error with Sitecore's validation? Or should I put the closing </div>
tags somewhere else?
Update:
Here is the method to add my rendering. It includes adding to RenderRendering's disposal list.
using Foundation.SitecoreExtensions.RenderingWrapper.Markers;
using Sitecore;
using Sitecore.Mvc.ExperienceEditor.Presentation;
using Sitecore.Mvc.Pipelines.Response.RenderRendering;
namespace Foundation.SitecoreExtensions.RenderingWrapper
{
public class AddEditorRenderingWrapper : RenderRenderingProcessor
{
public override void Process(RenderRenderingArgs args)
{
if (args.Rendered || Context.Site == null || !Context.PageMode.IsExperienceEditorEditing || args.Rendering.RenderingType == "Layout")
{
return;
}
var marker = GetMarker(args);
if (marker == null)
{
return;
}
args.Disposables.Add(new EditorRenderingWrapper(args.Writer, marker));
}
private static IMarker GetMarker(RenderRenderingArgs args)
{
IMarker marker = null;
var renderingItem = args.Rendering.RenderingItem;
if (renderingItem != null)
{
marker = new EditorComponentRenderingMarker(renderingItem.Name);
}
return marker;
}
}
}
EditorRenderingWrapper
is simply a class that inherits from Sitecore.Mvc.ExperienceEditor.Presentation.Wrapper and does not add any new code.
And Disposables
comes from Sitecore.Mvc.Pipelines.MvcPipelineArgs.Disposables