16

I am looking for a way to generate a CSS Id for a Component Presentation, which is guaranteed to be unique within the scope of the page. My initial thought is to make use of the ordinal position of the Component Presentation to generate this ID, which must exist within the scope of the Component Presentation as it will be used within the rendered markup. I am using a combination of VBScript Page Template with a modular Component Template using a Dreamweaver layout TBB.

While I know that I could potentially use RenderContext in the Page Template to keep track of the ordinal position of the "current" Component Presentation and then pick this up within the Dreamweaver TBB, the legacy Page Template is overly complex, which would make this approach quite messy. Similarly, updating all supported Component Templates to increment a render context variable is not feasible.

Is there any built-in Dreamweaver equivalent to VBScript's ComponentPresentation.OrdinalPosition? Or is there a preferable alternative method to generate a unique Id for a Component Presentation within a DWT?

Thanks.

8 Answers 8

13

I agree that ordinal position can be very useful, so I added this to the XSLT Mediator a long time ago, I also added something called regional position which gets the position within a group. To achieve this, I set a Context Variable just before calling the ComponentPresentation.Render() code using the Page Template called ORDINAL_POSITION as follows:

if (m_Engine.PublishingContext.RenderContext.ContextVariables.Contains("ORDINAL_POSITION"))
{
    int ordinalPosition = ((int)m_Engine.PublishingContext.RenderContext.ContextVariables["ORDINAL_POSITION"]) + 1;
    m_Engine.PublishingContext.RenderContext.ContextVariables["ORDINAL_POSITION"] = ordinalPosition;
}
else
{
    m_Engine.PublishingContext.RenderContext.ContextVariables.Add("ORDINAL_POSITION", 0);
}

You can then access the context variables from your DWT. This article by Nick may help with that: http://www.tridiondeveloper.com/get-and-set-variables-in-dwts

8

Sounds functionally similar to a requirement for unique identifiers for analytics. I previously wrote a post that summarizes some of the points in the other answers: http://www.createandbreak.net/2013/01/sdl-tridion-and-web-analytics-aka-how.html (with drawings :-) )

To get unique identifiers for component presentations within a given page, you will need:

  • Component
  • Component Template
  • Position on the page

For CSS, assuming component and template names are unique, this could be a combination of component and template names. Otherwise use Tridion Content Manager URIs (e.g. tcm:5-123) or the "unique" part after the first dash.

For elements that are only added to the page once (and maybe use the ID attribute), you can also consider identifying the "region," container component (if used), or even schema. It depends on the content model.

Note that dynamic component presentations make it more challenging--whatever adds or calls DCPs must add the position requirement.

Edit: I'm taking back the suggestion for URIs since these identifiers are easier to use if consistent across Publications and especially between Dev, Test, Acceptance, and Production (DTAP) environments.

7

Thanks for the responses, all. In light of Bart's answer, I have decided that I can make do with having the ordinal position of each Component Presentation, with respect to its Component Template. That is, rather than identify that Component Presentation x is the nth Component Presentation on the page, I identify that Component Presentation x is the nth Component Presentation on the page with Component Template y. This allows me to create multiple Component Presentations, with the same Component Template, without any risk of their CSS-Ids clashing.

This also allows me to sidestep the limitation of not having a compound Page Template available.

To solve this, I have created a TBB, which increments a given integer render context variable and makes it available in the package (in a similar manner to Chris' answer, but on a per-template basis):

 class IncrementContextVariable : TemplateBase
 {
    private string _contextVariable;
    private string _packageItem;

    public override void Transform(Engine engine, Package package)
    {
        Initialize(engine, package);
        ReadParameters(package);

        // Initialize to 1
        int value = 1;
        // If context variable already exists, increment it.
        if (engine.PublishingContext.RenderContext
            .ContextVariables[_contextVariable] != null)
        {
            if (!Int32.TryParse(engine.PublishingContext.RenderContext
                .ContextVariables[_contextVariable].ToString(), out value))
            {
                throw new InvalidCastException(String.Format("Package item \"{0}\" 
                    already exists and is not of type int!", _contextVariable));
            }
            value++;
        }
        // Finally, update context variable and add to package for use.
        engine.PublishingContext.RenderContext.ContextVariables.Remove(_contextVariable);
        engine.PublishingContext.RenderContext.ContextVariables
            .Add(_contextVariable, value);
        package.PushItem(_packageItem, _package
            .CreateStringItem(ContentType.Text, value.ToString()));
    }

    private void ReadParameters(Package package)
    {
        if (package == null) throw new ArgumentNullException("package");
        _contextVariable = package.GetValue("field_name");
        _packageItem = package.GetValue("item_name");
    }
}   

Then, my DWT looks like this:

<div id="TemplateName@@item_name@@">
    ...
</div>
7

The Dreamweaver Component Template you are referring to is actually a Dreamweaver Template Building Block, which is part of a Component Template.

Now in the Dreamweaver TBB, you have direct access to package variables, but the Page is not part of that for a Component Presentation. The reason behind this must be because a Component Presentation can also be previewed or published without a Page context.

So instead of trying to find ways to get to the ordinal position, why not use the URIs of both the Component and the Component Template for your unique id instead.

Unfortunately the Template ID is not directly available in a DWT TBB, so you will have to create a C# Fragment TBB, Assembly TBB or Custom Function (requires login) to expose that. You can use something like:

IdentifiableObject item = engine.PublishingContext.ResolvedItem.Item;
IdentifiableObject template = engine.PublishingContext.ResolvedItem.Template;
string uniqueId = string.Format("{0}_{1}", 
                                item.Id.ToString().Replace("tcm:", string.Empty), 
                                template.Id.ToString().Replace("tcm:", string.Empty));

And then push the uniqueId to the package or return it from the Custom Function.

4
  • 2
    Thanks Bart - I've clarified my terminology as per your comments. Your suggestion is something I've considered but doesn't cover the (admittedly unlikely) case where a content editor places two identical Component Presentations on the same page. Ideally, I would like to uniquely identify the Component Presentation itself, rather than a particular combination of its constituent parts. The tricky part, of course, is that this unique identifier must be available within the scope of the Component Presentation itself.
    – Ant P
    Feb 22, 2013 at 11:47
  • You could consider getting the Page from the RenderContext, but that still doesn't help you in the situation where there are two identical Component Presentations on the Page. The ordinal position is only available in the Page Template context. Feb 22, 2013 at 11:54
  • 1
    BTW, @@ID@@ gets you the Component Template ID in DWT. Not in template builder for some weird reason, but it works when publishing & previewing
    – Nuno Linhares
    Feb 22, 2013 at 14:49
  • Of course the combination of component id and template id is only going to give you a unique id if your other business rules enforce this. Yeah - OK - why would anyone put the same component presentation twice on the page? But still - it's not illegal. Aug 11, 2014 at 11:41
6

It looks like you are not iterating through each Component Presentation on the page in order, so TemplateRepeatIndex would not work for you.

One option might be to write a Dreamweaver custom function? This could generate a unique id for you based on the Component and Component Template item ids?

6

As a couple of the answers have hinted, this is a page template responsibility rather than a component template one. That's all very well; it's possible to have the page render have variables which the component render can increment (for example, see this recipe on Tridion practice), but frankly it's pretty involved and probably not worth the trouble.

If your only requirement is to avoid duplicate ids in the page, the simplest thing is probably just to generate a GUID and use that. The .NET framework has good support for this (System.GUID).

2

There's a number of ways this can be achieved:

  • Write a Dreamweaver function source that you call from your DWT that tracks this. You can initialize the source when the page starts rendering (Initialize is always called) and then keep track of it. This extension almost gets there... might be a good start.
  • Write the variable from the Page template instead. Something along the lines of
<!-- TemplateBeginRepeat name="Components" -->
  <div id="CP_@@TemplateRepeatIndex@@">@@RenderComponentPresentation()@@</div>
<!-- TemplateEndRepeat -->
  • If all you need is a unique string, and you don't really care what's in that string, just must be unique within the page, then this could work...
<div id="@@Math.random()@@"> <!-- some content --> </div>
3
  • Sorry to down vote this, but creating a random ID for CSS does not seem very helpful. Feb 23, 2013 at 3:11
  • 1
    It generates a unique ID, which is what the OP asked for... :)
    – Nuno Linhares
    Feb 23, 2013 at 3:20
  • If a unique ID is all he needs (which i feel like Chris is a bit odd) i would go for a unique serial like SQLServer uses in distributed databases, that way you know for sure it's unique as it uses many specifics like time/date, NIC etc as seed since a random might generate the same sequence over and over again :) I bet there is a .Net call for that already.
    – IngmarU
    Feb 24, 2013 at 19:46
-1

How about this type of functionality set up in the page template

https://stackoverflow.com/questions/3975290/c-sharp-produce-a-random-number-in-a-range

3
  • it would be helpful if you explained what you were suggesting instead of just posting a link to a stack overflow post. Feb 25, 2013 at 4:16
  • fair enough I guess I thought the stack overflow post was self explanatory the solution seems simple enough an iteration over a for loop using a random number to add a unique id to a html element Feb 26, 2013 at 5:48
  • 1
    The unique id approach here and in Nuno's answer work to make it easy to distinguish between elements and can address the requirement: "guaranteed to be unique within the scope of the page." But to be able to consistently create CSS class definitions (or track via analytics in a similar requirement), we probably need them to be a little friendlier and also not to change each time the template renders. Mar 8, 2013 at 19:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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