A fluent wrapper for the TagBuilder class can make it much easier to work with the dynamic placeholders introduced in Sitecore 9.
For developers like me, one of the most exciting new features of Sitecore 9 is dynamic placeholders. This is a functionality that has been provided by the community for quite some time, but now it is finally a built-in feature of the product.
Like the community implementations from which it was derived, Sitecore now provides a DynamicPlaceholder
method on the Sitecore helper class. However, the API that Sitecore provides in version 9 goes beyond any of the open source implementations that I have seen. One nice feature is that you can utilize a TagBuilder
object to wrap your placeholders.
@* Using with a basic TagBuilder *@ | |
@{ | |
var colDiv = new TagBuilder("div"); | |
colDiv.AddCssClass("col-sm-4"); | |
colDiv.GenerateId("featureCol"); | |
} | |
<div> | |
@Html.Sitecore().DynamicPlaceholder("feature", colDiv, 3, 3) | |
</div> | |
@* Using with a Func<DynamicPlaceholderRenderContext, TagBuilder> *@ | |
@functions { | |
TagBuilder CreateColBuilder(DynamicPlaceholderRenderContext context) | |
{ | |
var builder = new TagBuilder("div"); | |
builder.AddCssClass("col-sm-" + 12/context.PlaceholdersCount); | |
builder.GenerateId("featureCol" + context.Index); | |
return builder; | |
} | |
} | |
<div class="row"> | |
@Html.Sitecore().DynamicPlaceholder("feature", CreateColBuilder, 3, 4) | |
</div> |
The DynamicPlaceholder
method will accept either a TagBuilder
or a Func<DynamicPlaceholderRenderContext, TagBuilder>
. This is very helpful, but as you can see in the examples above, creating your TagBuilder is a little cumbersome. My first thought when seeing this was that it would be nice if TagBuilder had a fluent API. I certainly wasn’t the first person to have that thought. Here is amore complete implementation of a FluentTagBuilder class based on Matt Honecutt’s example:
public class FluentTagBuilder | |
{ | |
public TagBuilder InnerBuilder { get; private set; } | |
public FluentTagBuilder(string tagName) | |
{ | |
InnerBuilder = new TagBuilder(tagName); | |
} | |
public FluentTagBuilder AddCssClass(string cssClass) | |
{ | |
InnerBuilder.AddCssClass(cssClass); | |
return this; | |
} | |
public FluentTagBuilder GenerateId(string name) | |
{ | |
InnerBuilder.GenerateId(name); | |
return this; | |
} | |
public FluentTagBuilder MergeAttribute(string key, string value, bool replaceExisting = false) | |
{ | |
InnerBuilder.MergeAttribute(key, value, replaceExisting); | |
return this; | |
} | |
public FluentTagBuilder MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes, | |
bool replaceExisting = false) | |
{ | |
InnerBuilder.MergeAttributes(attributes, replaceExisting); | |
return this; | |
} | |
public FluentTagBuilder SetInnerText(string innerText) | |
{ | |
InnerBuilder.SetInnerText(innerText); | |
return this; | |
} | |
public override string ToString() | |
{ | |
return InnerBuilder.ToString(); | |
} | |
} |
And here are some extension methods that make it easier to use:
public static class FluentTagBuilderHelperExtensions | |
{ | |
public static FluentTagBuilder Tag(this HtmlHelper helper, string tagName) | |
{ | |
return new FluentTagBuilder(tagName); | |
} | |
public static HtmlString DynamicPlaceholder(this SitecoreHelper sitecoreHelper, string placeholderName, | |
FluentTagBuilder chrome, int count = 1, int maxCount = 0, int seed = 0) | |
{ | |
return sitecoreHelper.DynamicPlaceholder(placeholderName, chrome.InnerBuilder, count, maxCount, seed); | |
} | |
public static HtmlString DynamicPlaceholder(this SitecoreHelper sitecoreHelper, string placeholderName, | |
Func<DynamicPlaceholderRenderContext, FluentTagBuilder> chromeResolver, int count = 1, int maxCount = 0, | |
int seed = 0) | |
{ | |
return sitecoreHelper.DynamicPlaceholder(placeholderName, c => chromeResolver(c).InnerBuilder, count, | |
maxCount, seed); | |
} | |
} |
With these extensions, setting up your TagBuilder object is much more convenient:
@* Using with a basic TagBuilder *@ | |
<div> | |
@Html.Sitecore().DynamicPlaceholder("feature", | |
Html.Tag("div") | |
.AddCssClass("col-sm-4") | |
.GenerateId("featureCol"), | |
3, 3) | |
</div> | |
@* Using with a Func<DynamicPlaceholderRenderContext, TagBuilder> *@ | |
<div class="row"> | |
@Html.Sitecore().DynamicPlaceholder("feature", | |
ctx => @Html.Tag("div") | |
.AddCssClass("col-sm-" + 12 / ctx.PlaceholdersCount) | |
.GenerateId("featureCol" + ctx.Index), | |
3, 4) | |
</div> |
Enjoy!