Sitecore 'dynamic placeholders' with MVC
Asked Answered
L

2

24

I'm looking for a working Dynamic Placeholder solution in MVC. There are at least two good descriptions of this "pattern" for use with WebForms:

And I also found this blog explaining how to do it with MVC:

First I have tried to implement Techphoria's method (with GUIDs) using techniques from the MVC blogpost (extension of the SitecoreHelper) and I also tried implementing the last described method (uses number suffixes that are incremented Column_1, Column_2, etc).

With all the variations I tried I didn't succeed in creating a working solution. My placeholders don't get properly named (I ended up with strange placeholder structures, or placeholders repeating themselves).

Without going into the specifics of my attempts, I would like to know if anyone else has a working solution ready that I could use.

If I can't find an already working solution, I will describe my problem in more detail and see if I can get that to work.

Libradalibrarian answered 28/2, 2013 at 11:45 Comment(0)
G
42

I created this extension that creates dynamic placholders

public static class SitecoreHelper
{
    public static HtmlString DynamicPlaceholder(this Sitecore.Mvc.Helpers.SitecoreHelper helper, string dynamicKey)
    {
        var currentRenderingId = RenderingContext.Current.Rendering.UniqueId;
        return helper.Placeholder(string.Format("{0}_{1}", dynamicKey, currentRenderingId));
    }
}

It creates a placeholder with the guid in the name. I also created a step in the pipeline that extracts the guid, and checks for placeholder settings.

Code to get placeholder settings to the dynamic placeholder If you create a dynamic placeholder with @Html.Sitecore().DynamicPlaceholder("test") - the following code takes the setting from the placeholder settings named test

 /// <summary>
/// Handles changing context to the references dynamic "master" renderings settings for inserting the allowed controls for the placeholder and making it editable
/// </summary>
public class GetDynamicKeyAllowedRenderings : GetAllowedRenderings
{
    //text that ends in a GUID
    private const string DYNAMIC_KEY_REGEX = @"(.+)_[\d\w]{8}\-([\d\w]{4}\-){3}[\d\w]{12}";

    public new void Process(GetPlaceholderRenderingsArgs args)
    {
        Assert.IsNotNull(args, "args");

        string placeholderKey = args.PlaceholderKey;
        Regex regex = new Regex(DYNAMIC_KEY_REGEX);
        Match match = regex.Match(placeholderKey);
        if (match.Success && match.Groups.Count > 0)
        {
            placeholderKey = match.Groups[1].Value;
        }
        else
        {
            return;
        }
        // Same as Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings but with fake placeholderKey
        Item placeholderItem = null;
        if (ID.IsNullOrEmpty(args.DeviceId))
        {
            placeholderItem = Client.Page.GetPlaceholderItem(placeholderKey, args.ContentDatabase,
                                                             args.LayoutDefinition);
        }
        else
        {
            using (new DeviceSwitcher(args.DeviceId, args.ContentDatabase))
            {
                placeholderItem = Client.Page.GetPlaceholderItem(placeholderKey, args.ContentDatabase,
                                                                 args.LayoutDefinition);
            }
        }
        List<Item> collection = null;
        if (placeholderItem != null)
        {
            bool flag;
            args.HasPlaceholderSettings = true;
            collection = this.GetRenderings(placeholderItem, out flag);
            if (flag)
            {
                args.CustomData["allowedControlsSpecified"] = true;
                args.Options.ShowTree = false;
            }
        }
        if (collection != null)
        {
            if (args.PlaceholderRenderings == null)
            {
                args.PlaceholderRenderings = new List<Item>();
            }
            args.PlaceholderRenderings.AddRange(collection);
        }
    }
}

The following code removes the guid from the chrome data in the pageeditor

/// <summary>
/// Replaces the Displayname of the Placeholder rendering with the dynamic "parent"
/// </summary>
public class GetDynamicPlaceholderChromeData : GetChromeDataProcessor
{
    //text that ends in a GUID
    private const string DYNAMIC_KEY_REGEX = @"(.+)_[\d\w]{8}\-([\d\w]{4}\-){3}[\d\w]{12}";

    public override void Process(GetChromeDataArgs args)
    {
        Assert.ArgumentNotNull(args, "args");
        Assert.IsNotNull(args.ChromeData, "Chrome Data");
        if ("placeholder".Equals(args.ChromeType, StringComparison.OrdinalIgnoreCase))
        {
            string argument = args.CustomData["placeHolderKey"] as string;

            string placeholderKey = argument;
            Regex regex = new Regex(DYNAMIC_KEY_REGEX);
            Match match = regex.Match(placeholderKey);
            if (match.Success && match.Groups.Count > 0)
            {
                // Is a Dynamic Placeholder
                placeholderKey = match.Groups[1].Value;
            }
            else
            {
                return;
            }

            // Handles replacing the displayname of the placeholder area to the master reference
            Item item = null;
            if (args.Item != null)
            {
                string layout = ChromeContext.GetLayout(args.Item);
                item = Sitecore.Client.Page.GetPlaceholderItem(placeholderKey, args.Item.Database, layout);
                if (item != null)
                {
                    args.ChromeData.DisplayName = item.DisplayName;
                }
                if ((item != null) && !string.IsNullOrEmpty(item.Appearance.ShortDescription))
                {
                    args.ChromeData.ExpandedDisplayName = item.Appearance.ShortDescription;
                }
            }
        }
    }
}

Edit

The web.config include settings are included below:

<sitecore>
  <pipelines>

    <getPlaceholderRenderings>
      <processor 
        type="YourNamespace.Pipelines.GetPlaceholderRenderings.GetDynamicKeyAllowedRenderings, YourAssembly"
        patch:before="processor[@type='Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings, Sitecore.Kernel']"/>
    </getPlaceholderRenderings>

    <getChromeData>
      <processor
        type="YourNamespace.Pipelines.GetChromeData.GetDynamicPlaceholderChromeData, YourAssembly"
        patch:after="processor[@type='Sitecore.Pipelines.GetChromeData.GetPlaceholderChromeData, Sitecore.Kernel']"/>
    </getChromeData>

  </pipelines>
</sitecore> 
Gardiner answered 28/2, 2013 at 12:39 Comment(6)
I'm going to test this first thing tonight. Thanks!Libradalibrarian
NP - I updated with some code to modify the displayname of the placeholder for the editor, they don't need to see the guidGardiner
How is this possible, exactly what i was looking for! Thanks a bunch!Shortterm
Any idea what to do when the first repeating placeholder is not added to ContextService.Get().GetInstances<PlaceholderContext>()? Because both my repeating placeholders are given the same key, as the second doesn't see the first in the list.Inception
One correction to the previous question: ContextService.Get().GetInstances<PlaceholderContext>() is a stack, so it only allows you to find parent placeholder, not 'sibling' placeholders. How can this work? (I'm using Sitecore 8)Inception
Using the above code in Sitecore 8 I can still see the Guid's in the Editor. is it compatible with version 8?Gaal
E
0

I downloaded the Integrated Dynamic Placeholders package from the sitecore marketplace. When building your page it increments the placeholders with configurable suffix appended to the end of the placeholder key to make them unique, according to their order on the presentation layer. Worked out of the box for me.

Eutrophic answered 19/7, 2016 at 13:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.