Jeremy Davis
Jeremy Davis
Sitecore, C# and web development
Article printed from: https://blog.jermdavis.dev/posts/2014/next-step-relative-data-sources

Next step: Relative data sources...

Published 03 March 2014
Updated 31 July 2014

Having spent a bit of time thinking about relative Data Source Locations last week, it struck me that the logical extension of this is to allow the data sources of components themselves to be relative to the context item. This is particularly useful when you need a branch template, that will include some child items of a page and you want to pre-configure the page's presentation to display these children via the data sources of UI components.

And happily this is a pretty trivial change for Sublayouts.

It's a common pattern to develop a base class for sublayouts in order to share code for common tasks like fetching data source values, so it makes perfect sense to extend a bases class like that to manage this behaviour. The first thing it needs to do is find the Sublayout component that exposes the specified data source – which is always the parent object of your UI components. It also makes sense that some components will not need this behaviour, so we'll create a property that only does any work if we actually use it:

public class BaseSublayout : System.Web.UI.UserControl
{
    private Sublayout _sublayout = null;

    public Sublayout Sublayout
    {
        get 
        {
            if (_sublayout == null)
            {
                _sublayout = this.Parent as Sublayout;
            }

            return _sublayout;
        }
    }
}

					

With that in place, we can now write a similar property for fetching the data source. If the value of the SubLayout object's DataSource property starts with "./" then it's relative, and we can substitute the "." for the Sitecore path of the context item. And we can wrap that behaviour into the same sort of "only calculate it if it's used" property as we used above:

public class BaseSublayout : System.Web.UI.UserControl
{
    private Sublayout _sublayout = null;
    private string _dataSource = null;

    public Sublayout Sublayout
    {
        get 
        {
            if (_sublayout == null)
            {
                _sublayout = this.Parent as Sublayout;
            }

            return _sublayout;
        }
    }

    public string DataSource
    {
        get
        {
            if (_dataSource == null)
            {
                _dataSource = Sublayout.DataSource;
                if (_dataSource.StartsWith("./"))
                {
                    _dataSource = Sitecore.Context.Item.Paths.FullPath  + _dataSource.Substring(1);
                }
            }
            return _dataSource;
        }
    }
}

					

So now if we ask for the DataSource property when the current item is "/sitecore/Content/Home" and the data source for the component's binding is set to "./Headlines" then the result will be "/sitecore/Content/Home/Headlines".

You can then use this base class in a simple user control:

public partial class ExampleControl : BaseSublayout
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!string.IsNullOrWhiteSpace(base.DataSource))
        {
            Item itm = Sitecore.Context.Database.GetItem(base.DataSource);
            //
            // do something with the item
            //
        }
        else
        {
            //
            // component has no data source configured
            // possible error state?
            //
        }
    }
}

					

Interestingly, it turns out that you cannot do the same thing with Renderings easily – code inside Sitecore for displaying renderings detects that the relative data source path "does not exist" and hence hides the rendering. You can see this if you put Page Editor into Debug mode and look through the output that generates.

And it sounds like that could be something to investigate for the future...

↑ Back to top