Thursday 29 October 2015

Dynamic placeholders and tabs in Sitecore

The problem


One issue with using placeholders embedded inside Sitecore renderings is that a placeholder can only appear once on the page. If a placeholder with the same name is placed multiple times then Sitecore doesn't know which placeholder is being referenced when it comes to adding the placeholder's controls. If you are using renderings as reusable layouts then this can be restrictive. As an example, let's say your pages have places where you want to show three columns of renderings, and what renderings go inside each component will be decided on a per-page basis. You might create a rendering that looks like this
<div class="columncontainer">
    <div class="col-33">
        @Html.Sitecore().Placeholder("left column")
    </div>
    <div class="col-33">
        @Html.Sitecore().Placeholder("middle column")
    </div>
    <div class="col-33">
        @Html.Sitecore().Placeholder("right column")
    </div>
</div>

and you can then drop that rendering on any page where you want to show three things in columns. Great, but what if you want to show two of these on the same page? Maybe on top of each other giving two rows of three columns. There is nothing stopping you adding two of the renderings to the same page, but when you do you end up with this
<div class="columncontainer">
    <div class="col-33">
        @Html.Sitecore().Placeholder("left column")
    </div>
    <div class="col-33">
        @Html.Sitecore().Placeholder("middle column")
    </div>
    <div class="col-33">
        @Html.Sitecore().Placeholder("right column")
    </div>
</div>
<div class="columncontainer">
    <div class="col-33">
        @Html.Sitecore().Placeholder("left column")
    </div>
    <div class="col-33">
        @Html.Sitecore().Placeholder("middle column")
    </div>
    <div class="col-33">
        @Html.Sitecore().Placeholder("right column")
    </div>
</div>

Now when you tell Sitecore you want to put "Featured product" in the "middle column"...how does Sitecore know which "middle column" you are reffering to? There are two on the page. This limitation can also affect you if you want to do a flexible tab component. Let's say you want a rendering that can show tabbed content and you want the number of tabs to be dynamic. This does have a workaround, when you are creating your tabs you can give each placeholder a unique id based on its index.
<div class="tabs">
    <div class="tab">
        @Html.Sitecore().Placeholder("tab_1")
    </div>
    <div class="tab">
        @Html.Sitecore().Placeholder("tab_2")
    </div>
    <div class="tab">
        @Html.Sitecore().Placeholder("tab_3")
    </div>
</div>

That will work ok, but how do you control what renderings are valid to go inside each placeholder? What renderings can be placed where is configured in the Placeholder Settings section;

/sitecore/layout/Placeholder Settings

Here you create a "Placeholder" item for each placeholder and that item contains the placeholder key and a list of renderings that are valid for that placeholder. If you have placeholders like "tab_1", "tab_2 and "tab_3", then you will need to create 3 Placeholder items, one for each tab, and for each one defining what controls can go in that tab. Not only is this a lot of messy duplication, what happens when someone wants 4 tabs?