SPEAK: ItemTreeView with multiple Roots

I’ve being studying SPEAK recently for a module I’m building, and it’s being a lot of fun so far. Of course, when learning anything that imposes a different paradigm, it’s common to get stuck with “simple” things that, in another programming realities, are easy to accomplish.

And then it happened to me: the ItemTreeView component offers exactly the UI experience I want to provide. It lists Sitecore items in a Tree, just like the Content Editor, but also offers a way for the user to select items with checkboxes. That is perfect, but there’s one problem: you can only have one single Root for each ItemTreeView component.

ItemTreeView - Out of the Box

This limitation simply made impossible one requirement I have, which is to list a certain Template and all their fields and Standard Values (Ok) along with all their Base Templates (impossible). Since we can’t predict how many Base Templates (if any) a certain template has, we can’t simply add one or two ItemTreeViews to the SPEAK page and expect they will cover all your needs. Instead, we need to be able to dynamically add multiple Roots to an ItemTreeView. But how?

First Try – Google

As a good boy I first asked Google, but looks like SPEAK components are not yet very widely being subjects of debates in forums and communities worldwide. Most of the documentation are introductory step-by-steps, along with the official documentation which is well descriptive, but not very detailed in terms of what you can do with a certain SPEAK component. Nothing really targeting the ItemTreeView component in general, or my need of having multiple Roots in specific.

Second try – Sitecore Community

For those who don’t know yet, the new Sitecore Community website, built on top of Zimbra/Telligent Community, is the main resource for trading Sitecore experiences. After a quick search, I also see no entries discussing what I needed, and then decided to start up my own question topic: “ItemTreeView with SPEAK : Having multiple RootsItems? Dynamically adding a new one? (with C#)

Again nothing came out from that topic, so I started to think this may be a good thing to contribute, and at the same time to achieve what I’m trying to have in my component. This post brings the whole solution, along with a Sitecore package with the final code and items that you can download and use.

Ok, nevermind, I’ll build my own

The ItemTreeView component does not support multiple roots out-of-the-box, so I ended up solving this issue by creating a new version of the ItemTreeView component that does it.

Looking the original ItemTreeView component (core:/sitecore/client/Business Component Library/version 1/Layouts/Renderings/Lists and Grids/ItemTreeView) brought me to its View file (sitecoreshellclientBusiness Component LibraryLayoutsRenderingsListsAndGridsTreeViewsItemTreeView.cshtml).

These are the important parts we want to pay a special attention:

  • Line 17 – It’s where the rootId configured by the user is taken:
    var rootItemId = userControl.GetString("RootItem");
  • Lines 56 to 65 – RootId is used to retrieve the real Sitecore item:
    Item rootItem = null;
    if (!string.IsNullOrEmpty(rootItemId))
    {
        rootItem = database.GetItem(rootItemId, Language.Parse(contentLanguage));
    }
    if (rootItem == null)
    {
        rootItem = database.GetRootItem();
    }
  • Lines 67 to  69 – The TreeView component is setup with the Root Item configured
    var rootItemIcon = Images.GetThemedImageSource(!string.IsNullOrEmpty(rootItem.Appearance.Icon) ? rootItem.Appearance.Icon : "Applications/16x16/documents.png", ImageDimension.id16x16);
    userControl.SetAttribute("data-sc-rootitem", rootItem.DisplayName + "," + rootItem.Database.Name + "," + rootItem.ID + "," + rootItemIcon);
    userControl.SetAttribute("data-sc-rootitempath", rootItem.Paths.Path);
  • Lines 86 to 88 – The div container is output with its configurations
    <div @htmlAttributes>
    <ul></ul>
    </div>

My first experience were to duplicate the div markup, so I had it twice:

<div @htmlAttributes>
<ul></ul>
</div>
<div @htmlAttributes>
<ul></ul>
</div>

This ended up showing two identical roots – and the “twin” root worked perfectly. That was the confirmation I needed to create an improved version of the ItemTreeView component that can spit multiple roots. With the goal of simplicity, my component will act the same as the original ItemTreeView, but its “RootItem” property now will accept not just one, but multiple IDs (in a pipe-delimited string).

Here are the steps I take:

STEP 1 – Create the new component

  • Duplicated the original component and gave it the name “ItemTreeView2″
  • At the duplicated item, deleted the children “ItemTreeView Parameters” (we are going to use the original parameters template)
  • “Parameters Template” field – it should stay poiting to the original (Client/Business Component Library/version 1/Layouts/Renderings/Lists and Grids/ItemTreeView/ItemTreeView Parameters)
  • At the “Path” field I made it point to my new View file: /sitecore/shell/client/Business Component Library/Layouts/Renderings/ListsAndGrids/TreeViews/ItemTreeView2.cshtml

STEP 2 – Make it accept multiple Roots

  • Comment the original line:
    //var rootItemId = userControl.GetString("RootItem");
  • Add our new logic:
    var rootItemIds = userControl.GetString("RootItem").Split('|');
    var rootItemId = rootItemIds.FirstOrDefault();

Most of the original component will stay as is, and the first Root will be handled exactly how it is natively done.

STEP 3 – Make it spit the other Roots

  • At the very bottom of the View, we are going to create a new list of HtmlAttributes, which is here used to represent the Tree setup:
    @{
        var lstAttributes = new List<HtmlString>();
        for (var i = 1; i < rootItemIds.Length; i++)
        {
            rootItem = null;
            if (!string.IsNullOrEmpty(rootItemId))
            {
                rootItem = database.GetItem(rootItemIds[i], Language.Parse(contentLanguage));
            }
            if (rootItem == null)
            {
                rootItem = database.GetRootItem();
            }
            rootItemIcon = Images.GetThemedImageSource(!string.IsNullOrEmpty(rootItem.Appearance.Icon) ? rootItem.Appearance.Icon : "Applications/16x16/documents.png", ImageDimension.id16x16);
            userControl.SetAttribute("data-sc-rootitem", rootItem.DisplayName + "," + rootItem.Database.Name + "," + rootItem.ID + "," + rootItemIcon);
            userControl.SetAttribute("data-sc-rootitempath", rootItem.Paths.Path);
            lstAttributes.Add(userControl.HtmlAttributes); 
        } 
    }
  • And then have the loop for the HTML container:
    @foreach (var htmlAttr in lstAttributes)
    {
        <div @htmlAttr>
        <ul></ul>
        </div>
    }
    

And that’s all!

Now I can add a pipe-delimited list of IDs, either by adding it tothe rendering setup or by using my PageCode file:

TreeDsBaseTemplates.Parameters["RootItem"] = String.Join("|",
ComponentItem.DatasourceTemplate.BaseTemplates.Select(p => p.ID.ToString()).ToArray());
TreeDsBaseTemplates.Parameters["Database"] = ComponentItem.DatasourceTemplate.Database.Name;

This way my module now has two ItemTreeViews: first is a normal (native) one, the other is my extended control with roots being dynamically added:

ItemTreeView2

The package

Here is the resulting package of the new ItemTreeView2 SPEAK component. Fell free to download and use it!

Enjoy!

Publicado em Development, SPEAK

Leave a Reply

Your email address will not be published. Required fields are marked *

*

  Am Not Spammer

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>