On the Our vehicles page i would like to display three seperate div's (for each manufacturer) and within these divs i would like to display the manufacturerName and manufacturerInformation (pulled in from from the vehicle manufacturer doc type) and also a list of each manufacturer child nodes along with a coverImage and vehicleName which is pulled from vehicleProfile doc type.
So i have the following xslt (which may be a bit messy but kind of works)
My problem at the moment is the foreach loop to list the manufacturerChild nodes and inforamtion. It is just duplicating all the information and i cant work out why (this will be my lack of knowledge in xslt). This is what i am seeing
Hopefully this makes sense to someone as im really struggling to work out whats happening. Maybe i need two seperate xslt files?
This is a perfect "start from scratch with match templates" situation; have a look at this, and try it in a new clean macro just to see how it works:
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:umbraco.library="urn:umbraco.library"
exclude-result-prefixes="umbraco.library"
>
<xsl:output method="html" indent="yes" omit-xml-declaration="yes" />
<xsl:param name="currentPage" />
<xsl:variable name="siteRoot" select="$currentPage/ancestor-or-self::*[@level = 1]" />
<xsl:template match="/">
<!-- Start by processing the VehicleArea node, locating it from the $siteRoot -->
<xsl:apply-templates select="$siteRoot/VehicleArea" />
</xsl:template>
<xsl:template match="VehicleArea">
<section class="vehicles">
<h1><xsl:value-of select="@nodeName" /></h1>
<!-- Process the manufacturers -->
<xsl:apply-templates select="VehicleManufacturer" />
</section>
</xsl:template>
<xsl:template match="VehicleManufacturer">
<div class="manufacturer">
<h1><xsl:value-of select="@nodeName" /></h1>
<!-- Process the profiles for this -->
<xsl:apply-templates select="VehicleProfile" />
</div>
</xsl:template>
<xsl:template match="VehicleProfile">
<div class="profile">
<h2><xsl:value-of select="@nodeName" /></h2>
</div>
</xsl:template>
</xsl:stylesheet>
The idea is that each template takes care of "its own" data, so to speak... does that make sense?
Using nested for-each loops forces you to constantly place conditions etc. to make sure you have the right data, whereas the apply-templates instruction collects some items and finds the matching templates to render them with. If no items are found, nothing is rendered.
You can have matching templates for every property as well if necessary, e.g. for rendering coverImage and manufacturerImage consistently.
As always thanks for taking the time out to respond to my xslt related problem :).
I am due to leave work soon so i will try and implement this later at home and let you know how i get on. Everything that you have explained in your post seems clear enough so thanks for that.
Im using this code to display the vehicle profile images on the home page but i only want to display 4. I tried adding in a variable for maxitems to hold 4 and then sorting using the
Yeah you got it thats exactly what i am trying to achieve here. I am trying to reduce the vehicle profile nodes to only four items so for example i now have the following tree structure
and what i am trying to do is display only four of them (thats all i have room for) on the homepage. The xslt is generating the following
it is just displaying them all. :|. I have also tried using a random class but was getting errors so took that out lol. Just want to get it working first. I was sure my attempt wasnt too far away lol
<xsl:sort select="position() mod Exslt.ExsltMath:random()" order="descending" />
I get it now - you're not trying to limit the number of profiles per vehicle, but the total number of profiles shown - across vehicles. That's suddenly a grouping problem (which is one of the worst things to ask even an experienced XSLT developer to do - just ask around :-)
Ideally, you'd want to grab all profiles and then sort them somehow (e.g. by creation date, descending or some other criteria). This can be a potential problem because a lot of profiles will not be shown - and they can complain about it! :-)
So do you know what the criteria will be? Should it just be the first 4, essentially just using the order in Umbraco?
If so, you could just modify the VehicleArea template:
<xsl:template match="VehicleArea">
<!-- Process the manufacturers -->
<xsl:apply-templates select="VehicleManufacturer/VehicleProfile" />
</xsl:template>
- and keep the $maxItems stuff in the VehicleProfile template.
If you need to do sorting/grouping of some sorts, lets hear it and we'll figure that out too!
Basically it is my call on which vehicles i chosse to show but my thinking was to get all vehicle profiles e.g. 16 seater, ford 16 seater, E7 peugeout Taxi etc. group them together and then just randomly select any four from the group.
I have done something similar before by setting a max item and using the random math function but i am struggling to implement this using templates.
the code i used before looked similar to this
i declared the variable max items and then i set it to 4
xsl:variable name="maxItems" select="4" />
i then used a for each loop with a sort function
<xsl:sort select="position() mod Exslt.ExsltMath:random()" order="descending" />
this basically looped through all the nodes randomly ordered them into position and then selected the top 8 to be displayed.
I was thinking of something along the same lines for this site.
Alright — if you do that, you'll need to do the $maxItems test inside the for-each, like this:
<xsl:template match="VehicleArea">
<!-- Get all the profiles -->
<xsl:variable name="vehicleProfiles" select="VehicleManufacturer/VehicleProfile" />
<!-- Randomize and process the top $maxItems -->
<xsl:for-each select="$vehicleProfiles">
<xsl:sort select="position() mod Exslt.ExsltMath.random()" data-type="number" order="descending" />
<xsl:if test="position() <= $maxItems">
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="VehicleProfile">
<figure class="homepage-bus">
<xsl:if test="normalize-space(coverImage)">
<!-- Do stuff -->
</xsl:if>
</figure>
</xsl:template>
I've had some problems with EXSLT's random() function, though - returns the same number each time it's called within the same transformation... but I guess when used with the mod operator and position(), it kinda behaves somehow randomly :)
Otherwise, have a look at some of uComponents' random functions, which I've used a lot.
Thanks for getting back. As always mate your solution works perfectly. It does exactly what i need it to do. It's not really a big problem if they are not 100% randomised as long as there is some variation to give it a bit more of a dynamic feel.
Just so i understand this full can i just ask you to explain what this line does please?
No worries, the apply-templates instruction collects whatever nodes are selected by the expression in the select attribute and for each of those nodes, it finds the template to use (could be a built-in template or any of the custom ones defined in the stylesheet) and then, of course, instatiates the template.
The dot in the select expression refers to the current context node, which in the case of being inside a for-each loop, will be the current item. So the processor will see a single VehicleProfile node and then it'll find the template that should be used for rendering it.
The really powerful thing here is, that let's say you need to do something different depending on some property, you could just create a more specific template and the processor would automatically pick that template. For example, the way you test for a coverImage being present, could be handled by a separate template, like this:
This way, you're not polluting your "standard" output template with a condition, but you can make it focus on the actual output and have another template handle the missing image case. This is usually where "traditional" programmers start to lose their grip - somehow, some people have a hard time keeping track of the almost parallel control flow that this enables, but your mileage may vary, of course.
Hope that wasn't too much of a mouthful - have a nice day! :-)
Apologies for the late reply I been in a meeting all day!
Thank you very much for taking the time out to write a lengthy detailed reply. I much prefer more information because it's more for me to learn and take on board :) so keep it coming ;)
Thanks for the examples too it helps me understand things more.
Xslt for displaying childnode subnode information
Hi all,
this is quite a toughie to explain what i want to achieve so ill try use some screen shots to help you understand my issue.
So.. i have the following tree structure
Our vehicles = vehicleArea doctype Mercedes,Ford,Vauxhall = vehicleManufacturer doctype 16 seater, 8 seater, 6 seater = vehicleProfile doctype
On the Our vehicles page i would like to display three seperate div's (for each manufacturer) and within these divs i would like to display the manufacturerName and manufacturerInformation (pulled in from from the vehicle manufacturer doc type) and also a list of each manufacturer child nodes along with a coverImage and vehicleName which is pulled from vehicleProfile doc type.
So i have the following xslt (which may be a bit messy but kind of works)
My problem at the moment is the foreach loop to list the manufacturerChild nodes and inforamtion. It is just duplicating all the information and i cant work out why (this will be my lack of knowledge in xslt). This is what i am seeing
Hopefully this makes sense to someone as im really struggling to work out whats happening. Maybe i need two seperate xslt files?
Thanks
Paul
Hi Paul,
This is a perfect "start from scratch with match templates" situation; have a look at this, and try it in a new clean macro just to see how it works:
The idea is that each template takes care of "its own" data, so to speak... does that make sense?
Using nested for-each loops forces you to constantly place conditions etc. to make sure you have the right data, whereas the apply-templates instruction collects some items and finds the matching templates to render them with. If no items are found, nothing is rendered.
You can have matching templates for every property as well if necessary, e.g. for rendering coverImage and manufacturerImage consistently.
/Chriztian
Hey Chriztian,
As always thanks for taking the time out to respond to my xslt related problem :).
I am due to leave work soon so i will try and implement this later at home and let you know how i get on. Everything that you have explained in your post seems clear enough so thanks for that.
Ill promise to stop pestering you on twitter ha!
Cheers
Paul
Hi Chriztian,
Im using this code to display the vehicle profile images on the home page but i only want to display 4. I tried adding in a variable for maxitems to hold 4 and then sorting using the
Cheers
Paul
Hi Paul,
That should actually work - assuming that you're trying to limit the display of VehicleProfile nodes, and not e.g. the VehicleManufacturer nodes?
Are you saying that you have something like this:
-and you're not able to cap the list at only the first four items, leaving the ridiculous 19 seater off?
'coz that doesn't really make sense (unless you're not showing me all of the XSLT :-)
/Chriztian
Hi Chriztian.
Yeah you got it thats exactly what i am trying to achieve here. I am trying to reduce the vehicle profile nodes to only four items so for example i now have the following tree structure
and what i am trying to do is display only four of them (thats all i have room for) on the homepage. The xslt is generating the following
it is just displaying them all. :|. I have also tried using a random class but was getting errors so took that out lol. Just want to get it working first. I was sure my attempt wasnt too far away lol
Cheers
Paul
Aha :)
I get it now - you're not trying to limit the number of profiles per vehicle, but the total number of profiles shown - across vehicles. That's suddenly a grouping problem (which is one of the worst things to ask even an experienced XSLT developer to do - just ask around :-)
Ideally, you'd want to grab all profiles and then sort them somehow (e.g. by creation date, descending or some other criteria). This can be a potential problem because a lot of profiles will not be shown - and they can complain about it! :-)
So do you know what the criteria will be? Should it just be the first 4, essentially just using the order in Umbraco?
If so, you could just modify the VehicleArea template:
- and keep the $maxItems stuff in the VehicleProfile template.
If you need to do sorting/grouping of some sorts, lets hear it and we'll figure that out too!
/Chriztian
Hi Chriztian,
Basically it is my call on which vehicles i chosse to show but my thinking was to get all vehicle profiles e.g. 16 seater, ford 16 seater, E7 peugeout Taxi etc. group them together and then just randomly select any four from the group.
I have done something similar before by setting a max item and using the random math function but i am struggling to implement this using templates.
the code i used before looked similar to this
i declared the variable max items and then i set it to 4
i then used a for each loop with a sort function
this basically looped through all the nodes randomly ordered them into position and then selected the top 8 to be displayed.
I was thinking of something along the same lines for this site.
Hope that makes sense
Paul
Alright — if you do that, you'll need to do the $maxItems test inside the for-each, like this:
I've had some problems with EXSLT's
random()
function, though - returns the same number each time it's called within the same transformation... but I guess when used with the mod operator andposition()
, it kinda behaves somehow randomly :)Otherwise, have a look at some of uComponents' random functions, which I've used a lot.
/Chriztian
Morning Chriztian,
Thanks for getting back. As always mate your solution works perfectly. It does exactly what i need it to do. It's not really a big problem if they are not 100% randomised as long as there is some variation to give it a bit more of a dynamic feel.
Just so i understand this full can i just ask you to explain what this line does please?
Many thanks
Paul
Good mornin' :-)
No worries, the apply-templates instruction collects whatever nodes are selected by the expression in the
select
attribute and for each of those nodes, it finds the template to use (could be a built-in template or any of the custom ones defined in the stylesheet) and then, of course, instatiates the template.The dot in the select expression refers to the current context node, which in the case of being inside a for-each loop, will be the current item. So the processor will see a single VehicleProfile node and then it'll find the template that should be used for rendering it.
The really powerful thing here is, that let's say you need to do something different depending on some property, you could just create a more specific template and the processor would automatically pick that template. For example, the way you test for a coverImage being present, could be handled by a separate template, like this:
This way, you're not polluting your "standard" output template with a condition, but you can make it focus on the actual output and have another template handle the missing image case. This is usually where "traditional" programmers start to lose their grip - somehow, some people have a hard time keeping track of the almost parallel control flow that this enables, but your mileage may vary, of course.
Hope that wasn't too much of a mouthful - have a nice day! :-)
/Chriztian
Hi Christian,
Apologies for the late reply I been in a meeting all day!
Thank you very much for taking the time out to write a lengthy detailed reply. I much prefer more information because it's more for me to learn and take on board :) so keep it coming ;)
Thanks for the examples too it helps me understand things more.
Cheers again mate! Your a legend
Paul
is working on a reply...