Blog

Creating dependent MultiNode TreePickers (MNTPs)

The MultiNode TreePicker is a powerful Umbraco data type on its own, but what if you could have one MNTP that depended on the value of another? This was recently needed in a project I was working on and it's fairly simple to do, so I'm going to share it with you.

When talking about "dependent" MNTPs we mean that the first MNTP selection defines the starting (root) node of the second MNTP.

In my project, there was a "Works" node and under it several "Category" nodes, which contained project detail pages. Only one of those category nodes contained so-called "major" projects. We needed a way to define which subcategory node was the one that contained those "major" projects without hardcoding it or adding extra fields to the node. An MNTP that would allow us to choose one "category" node would easily do the job, so things were very simple up to here. 

Next, we needed to have a way to select some of the "major" project detail pages to create a list and display it on the website. Again, an MNTP would do the job here - but it would have to point to the "major" projects category as its start node, essentially using the selection from the first MNTP as its root node.

In order to demonstrate how it's done, I have created a similar scenario using the default Umbraco installation with the Fanoe starter kit. The Fanoe starter kit page structure is what's shown in the screenshot below:

In this screenshot, "Learn", "Explore", "Extend" are documents of type "LandingPage" and "The starter kit", "Basics" and "Masterclasses" are documents of type "TextPage". So landing pages can contain text pages.

In this scenario, we want to implement two dependent pickers. The first will allow the selection of a landing page while the second will use that landing page as its starting node allowing only for the selections of the text pages that are under it. It's analogous to the categories-  major projects scenario described above.

The two pickers will be placed in a tab on the home page, and the result will be like the one below:

And when we change the first picker's value:

So, the "secondary picker" gets its root node from what we selected on the "root picker". Let's see how our two pickers are configured.

The first picker is quite straightforward:

So this allows us to select only one document of type "LandingPage". This will be used as the second picker's starting (root) node.

The second picker goes like this:

We are using an XPath query to define the root node of that second picker and - guess what - it's based on the first picker's value.

Let's see what it does in detail:

The XPath query is:

$site//LandingPage[@id=./ancestor-or-self::node()[@id=./@id]/rootPicker/text()]

$site stands for "go up to the second topmost parent node" (which is usually our home page, while the topmost node is $root and it's the root of the whole content)

//LandingPage stands for "find any document of type "LandingPage" at any level under the homepage"

The predicate [@id=./ancestor-or-self::node()[@id=./@id]/rootPicker/text()] stands for "get the LandingPage node having an ID equal to the ID selected by the first MNTP".

Since this is too much to digest in one go, let's take the right part of the predicate apart and see what each part is doing:

./ancestor-or-self::node()[@id=./@id] is a way to say "current node" since the "$current" placeholder will not work in there. It actually says "bring me the node with an id equal to the current node's id" - which is the current node we're working on! I know, madness, but it's the only way to make it work :)

/rootPicker/text() is the value of the first MNTP. "rootPicker" is the property alias we gave to the first MNTP, and text() is the way to get its value since it's stored inside a CDATA node. 

So, if we needed to make it more generic, the parts in bold are the ones you will need to change depending on your setup:

$site//LandingPage[@id=./ancestor-or-self::node()[@id=./@id]/rootPicker/text()]

Please have in mind that, when using such dependent pickers with Umbraco 7, it's not enough to select the first picker's value and save or publish the page - you'll have to navigate AWAY from the page and come back for the second picker to change to its new root. This is not needed in Umbraco 6, but Umbraco 7 handles it differently. 



;