Blog

Getting the right starting node for MultiNode TreePicker with XPath

Getting the right starting node for MultiNode TreePicker with XPath

Have you ever used the MultiNode TreePicker? If you are creating sites in Umbraco, it's very possible that you have, multiple times. MNTP is one of the key Umbraco data types since it lets you select one or more nodes from a list and associate them with your current node.

MNTP also lets you define where your starting node (the root of the picker's subtree) will be, and you can define it either by hard-selecting a specific node or by using an XPath query. The latter is more efficient when you don't know the final structure of your site and/or you don't want to have any "hard-coded" node selections.

To make it easier, MNTP provides you with a set of "tokens" or, in Umbraco talk, "context-aware placeholders" that represent your current node ($current), your node's parent node ($parent), the topmost node in the current tree ($site) and the root node ($root). Okay, $site and $root are pretty simple. But $current and $parent are a totally different story.

The scenario

(Disclaimer: This is a very specific scenario to demonstrate the problem - not all scenarios are having the issue being discussed here)

Imagine this structure:

Blog (doctype: pageBlogList)
-- Posts
---- Post 1
---- Post 2
---- etc.
-- Categories
---- Category 1
---- Category 2
---- etc.
-- Authors (doctype: folderAuthors)
---- Author 1
---- Author 2
---- etc.

This structure corresponds to this very blog you are reading. So when creating a new blog post, we need to define a category and an author, selected from the "Categories" and "Authors" folders respectively. Using MNTP is a no-brainer here, since we have a list of things we want to associate with our post. 

The picker

The whole blog thing has to be reusable, so when creating the MNTP-based custom data type for the authors I didn't hard-select a node from which the picker would get nodes. Instead, I used an XPath query that said "I am a blog post, so go to my parent's parent (the blog "root"), then find the first child with a doctype of "folderAuthors" and use it as your starting point".

Or, in XPath talk,

"$parent/../folderAuthors[1]"

which can also be written as

"$current/../../folderAuthors[1]"

What is stable is that my post will always be two levels down from the blog page. I can have one single blog, one blog per language, or even multiple blogs within my site. Node IDs will change. The structure won't, so XPath was my preferred approach here, because it confines the "Authors" folder selection to the specific blog subtree.

The problem

I went to a blog post I had already written and published, added the Authors picker, and tried it. It worked OK, bringing me the list of nodes under the "Authors" folder. So everything was good, right?

No.

I then went on and created a new blog post, and BEFORE I saved or published it I tried the picker again. 

It would present me with the full site tree instead of the correct starting node. 

Why? Because $parent and $current cannot know a node that is not saved or published, and revert to its grandparent and parent node respectively (since they are the only ones it knows). And since there was no "Authors" folder directly under that node, MNTP would just ignore the whole thing and return the full site tree.

So we have $parent and $current return a different node, depending on whether the node using the picker is published or not. 

The solution

With that knowledge in mind, the XPath query can be altered to something that does NOT depend on the current node and will bring the same results in both cases. Such an XPath query for authors would be the following:

$current/ancestor-or-self::pageBlogList/folderAuthors[1]

This uses the "blog root" page as a reference point (the doctype is "pageBlogList"), so in plain English it says "no matter which the current node is, go up until you find the blog root, and then find the first child that has a doctype of "folderAuthors" and use it as a starting point.

Conclusion

When using XPath with MNTP you may not want to depend on $current and $parent directly, since the node they represent differs depending on whether the node using the MNTP is new (thus unpublished) or existing (thus published or saved). Instead, you need an approach that will work in both cases.

Of course, not all scenarios require this approach (for example, if you know you will only have one node with a specific doctype per site, you can do a "$site//myDocType" and be relatively safe. In the case described here, however, this would not cut it since we need the whole blog structure to be "portable", meaning that theoretically it could appear multiple times within the same site.



;