5

I have a rendering that can be placed in one of 3 placeholders. However, I need to make some changes to the rendering based on which placeholder it appears in. The changes aren't extensive enough to create 3 different renderings.

I'm using Sitecore 8.2 w/ MVC.

  • Are the differences only in the appearance? Or do you need to apply different code-level logic in the controller and the view based on the current placeholder? – Dmytro Shevchenko Dec 1 '16 at 14:42
  • Out of curiosity (and context), what are the differences between the 3 views? – jammykam Dec 1 '16 at 15:11
  • @jammykam The differences are mostly a CSS class difference. However in placeholder 1, there is additional HTML that goes around the entire block. – Kevin Dec 1 '16 at 15:29
4

You can make use of the Parameters field on the View/Controller Rendering templates to pass in some additional values and then use these in your View/Controller to then run whatever logic you need.

You still need to create 3 Rendering definition Items in Sitecore, but they can all point to the same View/Controller, but with the different parameters updated. On the View Rendering/Controller Rendering, set the Parameters field as required, e.g.:

Parameters: CssClass=special-highlighting&ExtendedInfo=true

Then in your code you can access these parameters:

@using Glass.Mapper.Sc.Web.Mvc
@using Sitecore
@using Sitecore.Mvc.Presentation
@model MyProject.Data.Interfaces.IGenericContent

@{
    RenderingParameters parameters = Sitecore.Mvc.Presentation.RenderingContext.CurrentOrNull.Rendering.Parameters;
    string cssClass = StringUtil.GetString((object) parameters["CssClass"], "default-class");
    bool showExtendedInfo = MainUtil.GetBool(parameters["ExtendedInfo"], false);
}

<div class="row @cssClass">
    <div class="large-12 columns">
        <h2>@Html.Glass().Editable(Model, x => x.Title)</h2>
        @Html.Glass().Editable(Model, x => x.Text)

        @if (showExtendedInfo)
        {
            @Html.Glass().RenderImage(Model, x => x.Image)
        }
    </div>
</div>

(The above example is using Glass Mapper, but it is not a requirement.)

The code is using Sitecore Helpers to set a default value if none has been set in the Parameters field. You can do something similar from your Controller as well.

Since you have 3 different Rendering Item definitions, you can restrict which rendering can be used in specific placeholders using Allowed Control restrictions.

  • This is exactly what I was looking for. – Kevin Dec 2 '16 at 13:21
4

You can reach your placeholder like this: RenderingContext.Current.Rendering.Placeholder. It's a string.

if (RenderingContext.Current.Rendering.Placeholder == "main")
   do stuff;
  • 1
    I commented on Dmytro, but same thing here. I don't want to hard-code the placeholder names. It just seems bad practice. I wish the placeholder settings in Sitecore had an additional field where I could pass across arbitrary values. – Kevin Dec 1 '16 at 14:56
  • 1
    I would create 3 different renderings. Same code, same controller, but 3 different entry methods. Minimal overhead, and makes the lives of your content editors easier as well - you can show them 3 different "logical" renderings that all end up as the same physical one. – Mark Cassidy Dec 1 '16 at 14:59
2

This might be overkill if you only need this functionality for one rendering, but it could give you the flexibility you seem to be looking for. The idea would be to extend renderings to have default personalization rules that apply to all instances of that rendering. Please note that I haven't actually tested this, it's just a concept.

  1. Create a template that inherits from Controller rendering. Add a Rules field and point the source field at /sitecore/system/Settings/Rules/Conditional Renderings.
  2. Create a processor for the getRenderingRules pipeline that looks something like this:
public class AddFromRenderingDefinition : GetRenderingRulesProcessor
{
    public override void Process(GetRenderingRulesArgs args)
    {
        Assert.ArgumentNotNull(args, nameof(args));
        var renderingItem = args.RenderingReference.RenderingItem;
        var rulesField = renderingItem.InnerItem.Fields["Personalization Rules"];
        if (rulesField != null)
        {
            var rules = RuleFactory.GetRules<ConditionalRenderingsRuleContext>(rulesField);
            args.RuleList.AddRange(rules.Rules);
        }
    }
}
  1. Patch it in:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getRenderingRules>
        <processor type="MyProject.AddFromRenderingDefinition, MyProject" patch:after="processor[@type='Sitecore.Analytics.Pipelines.GetRenderingRules.FromRendering, Sitecore.Analytics']"/>
      </getRenderingRules>
    </pipelines>
  </sitecore>
</configuration>
  1. Create a custom condition class to check if the rendering is in a particular placeholder like this:
public class InPlaceholderCondition<T> : StringOperatorCondition<T> where T : ConditionalRenderingsRuleContext
{
    public string Placeholder { get; set; }

    protected override bool Execute(T ruleContext)
    {
        Assert.ArgumentNotNull(ruleContext, nameof(ruleContext));
        return Placeholder != null && Compare(ruleContext.Reference.Placeholder, Placeholder);
    }
}
  1. Insert a Condition item under /sitecore/system/Settings/Rules/Definitions/Elements/Conditional Renderings and set the Type to the signature of your condition class and the Text to something like this:
where the placeholder [operatorid,StringOperator,,compares to] [Placeholder,,,value]
  1. Change the template on your Controller rendering items to the one created in step 1 and add your new condition and the Set parameters action, which should already be available.
1

Appearance-only changes

You can go with style changes only. First, in every place where you define a placeholder, you can add a wrapper with a certain class:

<section class="placeholder1-wrapper">
    @Html.Sitecore().Placeholder("placeholder1")
</section>

You can then apply styles to the elements in your rendering only if it is contained in that placeholder:

.placeholder1-wrapper .some-rendering-class {
    color: red;
}

Generic code-level approach

With this approach, keep in mind that modifying standard templates is not a good practice. This example uses the standard template for the sake of simplicity, but a better approach would be to make your custom placeholder settings template and put its items into a separate folder.

  1. Edit the template /sitecore/templates/System/Layout/Placeholder to add a custom field that would store a setting you'll get later.
  2. Create a placeholder setting item under /sitecore/layout/Placeholder Settings/ for each one of your placeholders and set the custom field to some value.
  3. Use the following code to get the setting value:
string placeholderName = RenderingContext.Current.Rendering.Placeholder.Split('/').Last();
string query = $"fast:/sitecore/layout/Placeholder Settings//*[@@templateid='{{5C547D4E-7111-4995-95B0-6B561751BF2E}}' and @@name='{placeholderName}']";
Item placeholder = Context.Database.SelectSingleItem(query);
string settingValue = placeholder["CustomPlaceholderSetting"];
  • Wouldn't work for MVC though – Mark Cassidy Dec 1 '16 at 14:48
  • I figured that I could just get the placeholder name, but that seems like a not best practices way. I was hoping that somewhere I could specifically assign a value to each placeholder within Sitecore that I could read and then determine how to alter my rendering. I also considered using CSS, but the differences are just enough that I can't do CSS only. – Kevin Dec 1 '16 at 14:52
  • @Kevin see my updated answer. – Dmytro Shevchenko Dec 1 '16 at 15:07
  • I saw that idea somewhere else, and as you suggested it's not best practice to modify the standard templates. Is there a proper way to extend the functionality of Sitecore system items? – Kevin Dec 1 '16 at 15:17
  • @Kevin I believe the only proper way is to create your own templates and items. – Dmytro Shevchenko Dec 1 '16 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.