Dynamic placeholders

Last updated Monday, April 23, 2018 in Sitecore Experience Platform for Developer
Keywords: Development

When you use dynamic placeholders, you can add the same placeholder name several times. You can do this across multiple renderings, and you can even use the same placeholder multiple times in a single rendering.

Dynamic placeholders mean that:

  • Unique keys are guaranteed across different renderings and within one rendering.
  • You can build functionality that generates multiple placeholders within the same rendering dynamically.

Using dynamic placeholders

The DynamicPlaceholder extension method is available in the SitecoreHelper class in the Sitecore.Mvc.Helpers namespace. The method has a number of overloads:

  • DynamicPlaceholder(string placeholderName, int count = 1, int maxCount = 0, int seed = 0)

    placeholderName - the name of the placeholder. Must be a non-empty string.

    count - specifies how many placeholders Sitecore renders. Must be a non-negative number.

    maxCount - the upper limit of the number of dynamically generated placeholders. Must be a non-negative number.

    seed - a number used to calculate a starting value of a generated placeholder key suffix. Must be a non-negative number:.

    Example:

@Html.Sitecore().DynamicPlaceholder("content"/*, optional parameters*/)

  • DynamicPlaceholder(string placeholderName, TagBuilder chrome, int count = 1, int maxCount = 0, int seed = 0)

    chrome - the TagBuilder object that specifies the HTML tag that will wrap each count of all dynamically generated placeholders.

  • DynamicPlaceholder (string placeholderName, Func<DynamicPlaceholderRenderContext, TagBuilder> chromeResolver, int count = 1, int maxCount = 0, int seed = 0)

    chromeResolver - the function that generates individual a TagBuilder wrapper for a specific dynamic placeholder, based on DynamicPlaceholderRenderContext.

  • DynamicPlaceholder(string placeholderName, Func<HtmlString, HtmlString> outputModifier, int count = 1, int maxCount = 0, int seed = 0)

    outputModifier - the function that accepts the HTML output of a specific placeholder and returns the modified HTML.

  • DynamicPlaceholder(string placeholderName, Func<HtmlString, DynamicPlaceholderRenderContext, HtmlString> outputModifier, int count = 1, int maxCount = 0, int seed = 0)

    outputModifier - the function that accepts the HTML output of a specific placeholder, DynamicPlaceholderRenderContext and returns the modified HTML.

  • DynamicPlaceholder(DynamicPlaceholderDefinition definition)

    definition - aggregates all options of defining a dynamic placeholder

Unlike a regular Sitecore placeholder that uses placeholder name as a placeholder key, a dynamic placeholder generates a unique key. The dynamic placeholder key looks like this:

{placeholder key}-{rendering unique suffix}-{unique suffix within rendering}

Where

  • rendering unique suffix - UID of a rendering that contains the placeholder. It guarantees a uniqueness to placeholder key across different renderings.
  • unique suffix within rendering - index number of a dynamically generated placeholder. First number equals to the Seed.

Assume that the dynamic placeholder content is used within the rendering with UID {7a943e27-b649-400c-986d-33d07f0f50ca}, count 2, and seed is 5. Sitecore generates these placeholder keys :

  • content-{7a943e27-b649-400c-986d-33d07f0f50ca}-5
  • content-{7a943e27-b649-400c-986d-33d07f0f50ca}-6

This is an example of using DynamicPlaceholderDefinition:

<div style="width: 100%;">
  <ol>
    @Html.Sitecore().DynamicPlaceholder(new DynamicPlaceholderDefinition("ListItem")
    {
      Count = 5,
      MaxCount = 10,
     Seed = 100,
      OutputModifier = (input, context) => new HtmlString("<li>" + input + "</li>"),
    })
  </ol>
</div>

This code generates five dynamic placeholders with ListItem as the key. A user can add a total of 10 placeholders through the rendering parameters. Each placeholder will be wrapped with <li> </li> tags.

You can improve the user experience with proper OutputModifier and use lambdas like this:

OutputModifier = (input, context) => string.Format("<div data-index=\"{0}\" data-dynamicId=\"{1}\" style=\"display: inline-block; width: 100px;\">", context.Index, context.DynamicKey)

This code gives the following HTML for the second dynamically rendered placeholder:

<div index="1" dynamicId="GridCell-{rendering UID}-101" style="display: inline-block; width: 100px;">

Customizing dynamic placeholders from rendering parameters

You can customize dynamic placeholders with additional rendering parameters. This allows you to specify the amount of placeholders that Sitecore generates dynamically. Parameters can be read from the DynamicPlaceholderRenderContext.Parameters dictionary that you pass, for example, to the OutputModifier.

For example, this means that you can override the Count parameter from the DynamicPlaceholderDefinition,so that you can update the Count parameter from the Content Editor or the Experience Editor without changing the code.

The parameter name formats are these:

  • ph_placeholderKey_paramName

    Example:

    Name: ph_column_count; value: 5. "column" dynamic placeholder will produce 5 placeholders.

    Name: ph_column_width; value: 30px

  • ph_placeholderKey_indexNumber_paramName

    Example:

    Name: ph_column_2_width; value: 50px. The "width" parameter is passed only while the placeholder with index 2 is processed by the output modifier.

Customizing key generation

You can customize the way Sitecore generates dynamic placeholder keys with the mvc.getDynamicPlaceholderKeys pipeline. This pipeline has three default processors:

  • GetRenderingUniqueSuffix

    The objective of this processor is to allow a placeholder key to be unique across a page. Returns the UID of a rendering that contains the generated placeholder.

  • GetUniqueKeysWithinRendering

    The objective of this processor is to allow a placeholder key to be unique within a concrete rendering. Returns the index number of a dynamic placeholder within the parent rendering. It generates this value each time during the rendering process. The first value is taken from the Seed and then it increments it.

  • AggregatePlaceholderKeys

    This processor returns the aggregates placeholder key, rendering unique suffix and unique suffix within rendering in the this format: {placeholder key}-{rendering unique suffix}-{unique suffix within rendering}. For example: GridCell-{7a943e27-b649-400c-986d-33d07f0f50ca}-0).

If you change the mvc.getDynamicPlaceholderKeys pipeline you must also update the mvc.getDynamicPlaceholderInitialKey pipeline. This pipeline is responsible for getting the initial placeholder key by the given dynamic placeholder key. The current implementation is based on RegExp -{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}}-[0-9]+$ and it tries to cut off the unique placeholder key suffix.

Known limitations

Dynamic placeholders have a number of limitations you must be aware of:

  • By default, placeholder settings work only with the initial dynamic placeholder key.

    For example, the dynamic placeholder @Html.Sitecore().DynamicPlaceholder("column", 2) becomes:

    @Html.Sitecore().Placeholder("column-{…}-0")

    @Html.Sitecore().Placeholder("column-{…}-1").

    The user can define a placeholder setting only for the column placeholder, and that be applicable both for content-{…}-0 and content-{…}-1. Defining the placeholder setting for a specific unique placeholder key (such as column-{…}-1) will have no effect, unless the Sitecore.Mvc.Pipelines.Response.GetDynamicPlaceholderInitialKey.RemovePlaceholderUniqueKeySuffix processor is enabled.

    Once mode is set to off: <mvc.getDynamicPlaceholderInitialKey><processor mode="off" type="Sitecore.Mvc.Pipelines.Response.GetDynamicPlaceholderInitialKey.RemovePlaceholderUniqueKeySuffix, Sitecore.Mvc"/></mvc.getDynamicPlaceholderInitialKey> it is possible to define placeholder settings for specific unique placeholder key, but now not for the initial key of a dynamic placeholder.

  • Each independent call of @Html.Sitecore().DynamicPlaceholder(…)knows nothing about each other. That's why if you insert two dynamic placeholders with the same name into the same rendering, you will get a collision. Example:

    @Html.Sitecore().DynamicPlaceholder("column", count: 2)

    @Html.Sitecore().DynamicPlaceholder("column")

    becomes:

    Html.Sitecore().Placeholder("column-{…}-0")

    Html.Sitecore().Placeholder("column-{…}-1")

    Html.Sitecore().Placeholder("column-{…}-0")

    To get rid of collision in this situation, it's necessary to set the Seed:

    @Html.Sitecore().DynamicPlaceholder("column", count: 2)

    @Html.Sitecore().DynamicPlaceholder("column", seed: 10)

    becomes:

    Html.Sitecore().Placeholder("column-{…}-0")

    Html.Sitecore().Placeholder("column-{…}-1")

    Html.Sitecore().Placeholder("column-{…}-10")

  • Note that you can override the Count parameter through the rendering parameters at any time, so you must make sure that the seed is set with room for the count increase or set the MaxCount.

Send feedback about the documentation to docsite@sitecore.net.