I "think" this is a bit of a tricky one - however I'm sure the rocket scientists here already have a solution :)
In our Umbraco rollouts - we give the administrators the ability override the node node-name in navigation. This allows administrators to keep the URL/name intact when you are on-page, but allows a shorter name to be used as part of a navigation taxonomy.
For example, I may have a page named "How to tie your shoe laces", however the navigation text for this item may be shortened "Tie shoe laces". (hope this makes sense).
This is all well and good, however I have the issue of ording navigation nodes if this feature is used on "some" but not "all" nodes. The following table illustrates this issue:
ID / Node Name / Text override / Name Displayed 1 / A / Y / Y 2 / B / X / X 3 / C / / C 4 / D / Z / D
I would like the data to be sorted as follows: 3 / C / / C 2 / B / X / X 1 / A / Y / Y 4 / D / Z / Z
Desired Sort The sort that I am looking for is that the nodes are sorted firstly by "Text override" (if entered) and then by "Node Name". In c# I would achieve this with a coalesce(textOveride,NodeName), however within the xslt environement and the simple "sort" element I cannot work out how this would be achieved.
I appreciate my query is very specific to overiding the node-name on navigation; however I can think of many other situations where ordering by multiple fields (optionally completed) would be extremely useful.
Unfrotuantely I have already tried this using both apply-template and for-each. I have also confirmed that I am querying the correct fields by outputting both the nodeName and textOverride values. Unfortunately the nodes without a textOverride are all sorted together, for example...
Sort using two sort nodes ID / Node Name / Text override / Name Displayed 2 / B / X / X 1 / A / Y / Y 4 / D / Z / Z 5 / E / / E <---- These two fields go to the end as their text override is blank 3 / C / / C<---- ~~~~~~~~~~~~~~~~~~~ " ~~~~~~~~~~~~~~~~~~~~~~~
Simply - this makes a string of the override text + the actual nodeName, e.g. nodeName versus TextOverrideNodeName.
Let's see how this plays out in the example I have given above...
ID / NodeName / TextOverride / ConCat(TextOverride,NodeName) 3 / C / / C 5 / E / / E 2 / B / X / XB 1 / A / Y / YA 4 / D / Z / ZD
PERFECT !!!! :)
Please let me know if there is a better way of doing this or if this helps you at all. I'm hoping that this little technique will help anyone who needs to sort by two textual valuals where one is an override of the other.
My, frankly less than perfect, solution above was definately not ideal and would actually have sorting issues when concatenating short words.
I would be very interested in understanding exactly how that syntax works... as my predicates didn't seem to work. I suspect this was becuase I had nested predicates like this...
but the [current()/data[@alias='mkNavText'] = ''] will make sure the @nodeName will only be selected if data[@alias='mkNavText'] is empty. Now you only get 1 result! the override or the nodename.
(it does not really make a <nodeset> structure, its for illustrational purposes)
What u did wrong was your xpath selecor:
@nodeName[./data[@alias='mkNavText']='']
this wil look for:
$page/ data[@alias='mkNavText']/data[@alias='mkNavText']
& $page/ @nodeName/data[@alias='mkNavText']
So in stead of the ./data i refer to current()/data. the current() references to the current item in the for-each loop
Sorting on two values (when one is an override)
I "think" this is a bit of a tricky one - however I'm sure the rocket scientists here already have a solution :)
In our Umbraco rollouts - we give the administrators the ability override the node node-name in navigation. This allows administrators to keep the URL/name intact when you are on-page, but allows a shorter name to be used as part of a navigation taxonomy.
For example, I may have a page named "How to tie your shoe laces", however the navigation text for this item may be shortened "Tie shoe laces". (hope this makes sense).
This is all well and good, however I have the issue of ording navigation nodes if this feature is used on "some" but not "all" nodes. The following table illustrates this issue:
ID / Node Name / Text override / Name Displayed
1 / A / Y / Y
2 / B / X / X
3 / C / / C
4 / D / Z / D
I would like the data to be sorted as follows:
3 / C / / C
2 / B / X / X
1 / A / Y / Y
4 / D / Z / Z
Desired Sort
The sort that I am looking for is that the nodes are sorted firstly by "Text override" (if entered) and then by "Node Name". In c# I would achieve this with a coalesce(textOveride,NodeName), however within the xslt environement and the simple "sort" element I cannot work out how this would be achieved.
I appreciate my query is very specific to overiding the node-name on navigation; however I can think of many other situations where ordering by multiple fields (optionally completed) would be extremely useful.
Many thanks for your help in adance,
David :-)
Hi David,
Hint: You can add as many sort elements inside an apply-templates (or for-each) instruction as you like...
(If that's not enough let us know :-)
/Chriztian
Hi Chriztian,
Thanks for coming back to me.
Unfrotuantely I have already tried this using both apply-template and for-each. I have also confirmed that I am querying the correct fields by outputting both the nodeName and textOverride values. Unfortunately the nodes without a textOverride are all sorted together, for example...
Sort using two sort nodes
ID / Node Name / Text override / Name Displayed
2 / B / X / X
1 / A / Y / Y
4 / D / Z / Z
5 / E / / E <---- These two fields go to the end as their text override is blank
3 / C / / C <---- ~~~~~~~~~~~~~~~~~~~ " ~~~~~~~~~~~~~~~~~~~~~~~
Hope this makes sence with my ASCII art table :-)
If overide is blanc use nodename ?
<xsl:sort select="menuTitle | @nodeName [menuTitle = '']" order="ascending" />
Hi Niels,
Thanks for the reply.
Unfortunately I coulndt undersatnd your reply - but it got me thinging about concatenating in the sort node itself.
I'm pretty sure that the following code is now working for me:
Simply - this makes a string of the override text + the actual nodeName, e.g. nodeName versus TextOverrideNodeName.
Let's see how this plays out in the example I have given above...
ID / NodeName / TextOverride / ConCat(TextOverride,NodeName)
3 / C / / C
5 / E / / E
2 / B / X / XB
1 / A / Y / YA
4 / D / Z / ZD
PERFECT !!!! :)
Please let me know if there is a better way of doing this or if this helps you at all. I'm hoping that this little technique will help anyone who needs to sort by two textual valuals where one is an override of the other.
Kind regards,
David
Hey David,
Good thing u got ya code running.
The most clean solution would be this:
<xsl:sort select="data[@alias='mkNavText'] | @nodeName [current()/data[@alias='mkNavText'] = '']"/>
Hi Niels,
That's better than PERFECT - thank you so much.
My, frankly less than perfect, solution above was definately not ideal and would actually have sorting issues when concatenating short words.
I would be very interested in understanding exactly how that syntax works... as my predicates didn't seem to work. I suspect this was becuase I had nested predicates like this...
<xsl:value-of select="./data[@alias='mkNavText'] [ ./data[@alias='mkNavText']!='' ] | @nodeName [ ./data[@alias='mkNavText']='' ]"/>
I'm marking your reply as the solution now, but if you don't mind - could you explain your elegant syntax to me?
Thank you again so much for you help,
David (happy)
Ok i wil try:
the " | " character will take the value left and value right and add it to a node-set, so it becomes like this:
<nodeset>
<value>data[@alias='mkNavText']<value>
<value>@nodeName [current()/data[@alias='mkNavText'] = '']</value>
</nodeset>
but the [current()/data[@alias='mkNavText'] = ''] will make sure the @nodeName will only be selected if data[@alias='mkNavText'] is empty. Now you only get 1 result! the override or the nodename.
(it does not really make a <nodeset> structure, its for illustrational purposes)
What u did wrong was your xpath selecor:
@nodeName [ ./data[@alias='mkNavText']='' ]
this wil look for:
$page/ data[@alias='mkNavText']/data[@alias='mkNavText'] &
$page/ @nodeName/data[@alias='mkNavText']
So in stead of the ./data i refer to current()/data. the current() references to the current item in the for-each loop
@nodeName [ ./data[@alias='mkNavText']=''"] = $page/ @nodeName/data[@alias='mkNavText']
&
@nodeName [ current()/data[@alias='mkNavText']='' ] = $page /data[@alias='mkNavText']
Fantastic, Niels, this just saved me a lot of hair tearing!
is working on a reply...