Display content from each child node seperately on the parent page
Hi All,
I am trying to display the content of each child node of the parent on the parent page. I have a page named:
Work (parent node)
Creative (child)
Business (child)
Market (child)
within each child i have project items for each node, now i can get all the project items to show on the parent page, but i want it to display each child node content on seperate lines e.g.
Creative
{projectItem} {projectItem} {projectItem}
Business
{projectItem} {projectItem} {projectItem}
Market
{projectItem} {projectItem} {projectItem}
Styled in Div's, i know it is probably something simple.
In my XSLT file i have
$currentPage/ProjectGroupPage/ProjectItem
and this displays them all from all nodes,
my aim is to have
$currentPage/Creative/ProjectItem and so on for each.
The trick is to create simple templates to handle each of the elements you need, e.g. somethiing like this:
<xsl:template match="/">
<!-- Process all ProjectGroupPage children of $currentPage -->
<xsl:apply-templates select="$currentPage/ProjectGroupPage" />
</xsl:template>
<xsl:template match="ProjectGroupPage">
<h2><xsl:value-of select="@nodeName" /></h2>
<!-- Process all ProjectItem children of this ProjectGroupPage -->
<xsl:apply-templates select="ProjectItem" />
</xsl:template>
<xsl:template match="ProjectItem">
<div class="item">
<a href="{umbraco.library:NiceUrl(@id)}">
<img id="project-home-image" src="/imageGen.ashx?image={RF-Slide/image}&width=172&height=166" alt="{title}" />
</a>
<a href="{umbraco.library:NiceUrl(@id)}">
<h2><xsl:value-of select="@nodeName"/></h2>
</a>
</div>
</xsl:template>
Now - it's not totally obvious if all your wrappers are of document type ProjectGroupPage or some are of other doctypes - if they are, you can match more than one type in a template:
I would like to have only a certain number of the project items showing on the page i.e first 4/5 i have implemented this for blog feeds in razor but not xslt.
to extend this i would like to be able to add seperate styling to the first projectitem of each section. would i do this by adding another template and selecting the position of the 1st item and applying a class to it?
Thank you in advance. you are very helpful and i'm learning a lot by breaking it down.
To do that, one needs just a little information about how the position() function works - it returns the position of the "current node" within the "current node list" (neither of these have anything to do with Umbraco's $currentPage parameter). The current node is the ProjectItem element when you're inside that template; or if you're inside a for-each "loop" it's the current node in the loop.
The current node list is all the nodes selected by the select attribute in an apply-templates or a for-each instruction.
So, in your current scenario, if you were to print the value of position() within the for-each loop, you'd get 1, 2, 3, 4, 5, 6, 7 etc. - but if you print the value from within the ProjectItem template, you'll get 1, 1, 1, 1 etc. - because we only apply-templates to a single element (the current) in the for-each.
The way I usually handle this is by sending an index parameter into the template - like this:
<xsl:template match="/"> <!-- Process all ProjectGroupPage children of $currentPage --> <xsl:apply-templates select="$currentPage/ProjectGroupPage" /> </xsl:template>
<xsl:template match="ProjectGroupPage"> <h1><xsl:value-of select="@nodeName" /></h1> <!-- Process all ProjectItem children of this ProjectGroupPage --> <xsl:apply-templates select="ProjectItem" /> <xsl:for-each select="ProjectItem"> <xsl:sort select="@createDate" data-type="text" order="descending" /> <xsl:if test="position() <= 4"> <xsl:apply-templates select="."> <xsl:with-param name="index" select="$position()" /> </xsl:apply-templates> </xsl:if> </xsl:for-each> </xsl:template> <xsl:template match="ProjectItem"> <xsl:param name="index" /> <div class="item"> <xsl:if test="$index =1"> <!--override the class attribute on first item --> <xsl:attribute name="class">first item</xsl:attribute> </xsl:if> <a href="{umbraco.library:NiceUrl(@id)}"> <img id="project-home-image" src="/imageGen.ashx?image={RF-Slide/image}&width=172&height=166" alt="{title}" /> </a> <a href="{umbraco.library:NiceUrl(@id)}"> <h2><xsl:value-of select="@nodeName"/></h2> </a> </div> </xsl:template> </xsl:stylesheet>
It gives me this error: (I looked into the with-param element and checked it seems to look fine. )
Error occured
Error in XSLT at line 33, char 11 31: <xsl:if test="position() <= 4"> 32: <xsl:apply-templates select="."> 33: >>> <xsl:with-param name="index" select="$position()" /> <<< 34: </xsl:apply-templates> 35: </xsl:if>
I resized the image for the first item through css, is it possible for me to apply the img for the first item within that code to have the imageGen on there to resize the first to the new size 280px wide? like it is underneath.
I set the height and width i wanted the images to be in the css and then in the xslt i set the imageGen width and height to 100% and it worked perfect :)
I want to specifically target each section within the xslt so that say i have 3 sections
Marketing
<projectItem> <projectItem> <projectItem> <projectItem> more..links to projectgrouppage = marketing
Creative
<projectItem> <projectItem> <projectItem> <projectItem> <more..links to projectgrouppage = creative>
Lead
<projectItem> <projectItem> <projectItem> <projectItem> <more..links to projectgrouppage = lead>
i want to add a small <more..> link on the end of each section to go to the parent page (ProjectGroupPage) of the (ProjectItems) within each section.
I'm guessing i could add this as another attribute after the initial attribute i already have linking to the parent ?
Sorry to be a pain, but I'm spending quite some time trying to get 2 of those project items any it doesn't matter to display on the homepage. I can either get 2 of each projectgrouppage project items to display or all of the project items. It seems to ignore the sorting i put in there. This is what i have ..
It just seems like you're a little confused about apply-templates, for-each and templates. No worries, you'll grok it eventually!
This is how you'd put it together - the comments should help you with what's going on:
<!-- Grab all the relevant ProjectItems -->
<xsl:variable name="projectItems" select="$currentPage/OurWork/ProjectGroupPage/ProjectItem" />
<xsl:template match="/">
<!-- Only show the latest 2 project items -->
<xsl:for-each select="$projectItems">
<xsl:sort select="@createDate" data-type="text" order="descending" />
<xsl:if test="position() <= 2">
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- This is the template for a single item -->
<xsl:template match="ProjectItem" >
<div class="home-item">
<!-- You know this... -->
</div>
</xsl:template>
Please note that just by extracting the long select from the for-each into a variable, that it's suddenly much easier to "read" what's going on, right?
It's much easier looking at it like that! I seem to confuse myself and make it so complicated and lost! This helps alot so thank you!
Since then the projects to display on the homepage have changed slightly, as i was using a multinode picker then i swapped to the one above to come in automatically but now they want the treepicker back so they can select 4 items for the homepage with the first having its own style, I'm pretty sure i can implement the param for the class to the first item, i have had the multinode working when my project items were originally under one node <our work> under <homepage> but now i have child sections<projectgrouppage> under the <our work> and the project items are under each of those project group pages, i get the node to show all of those but for some reason they are all red and won't drag over.
This is what i have (it's strange because one minute it was only displaying the <our work > node and the next it was displaying the children underneat, so i'm unsure whether it's something i'm doing or it's basically just glitchy :S
I was wrong, it seems i am struggling with where to place the with-param function as i'm now looking at unfamiliar elements with the multinode picker, my bad!
Display content from each child node seperately on the parent page
Hi All,
I am trying to display the content of each child node of the parent on the parent page. I have a page named:
Work (parent node)
Creative (child)
Business (child)
Market (child)
within each child i have project items for each node, now i can get all the project items to show on the parent page, but i want it to display each child node content on seperate lines e.g.
Creative
{projectItem} {projectItem} {projectItem}
Business
{projectItem} {projectItem} {projectItem}
Market
{projectItem} {projectItem} {projectItem}
Styled in Div's, i know it is probably something simple.
In my XSLT file i have
$currentPage/ProjectGroupPage/ProjectItem
and this displays them all from all nodes,
my aim is to have
$currentPage/Creative/ProjectItem and so on for each.
this is what i have so far.
version="1.0" encoding="UTF-8"?>
xsl:stylesheet [
]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:autofolders.library="urn:autofolders.library" xmlns:uTube.XSLT="urn:uTube.XSLT"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets autofolders.library uTube.XSLT ">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:template match="/">
<xsl:for-each select="$currentPage/Creative/ProjectItem">
<div class="item">
<a href="{umbraco.library:NiceUrl(@id)}">
<img id="project-home-image">
<xsl:attribute name="src">
<xsl:text>/imageGen.ashx?image=xsl:text>
<xsl:value-of select="RF-Slide/image"/>
<xsl:text>&width=172&height=166xsl:text>
xsl:attribute>
<xsl:attribute name="alt">
<xsl:value-of select="title"/>
xsl:attribute>
img>
a>
<a href="{umbraco.library:NiceUrl(@id)}">
<h2>
<xsl:value-of select="@nodeName"/>
h2>
a>
div>
xsl:for-each>
xsl:template>
xsl:stylesheet>
Any help would be greatly appreciated!
Thanks
Hi Molly,
The trick is to create simple templates to handle each of the elements you need, e.g. somethiing like this:
Now - it's not totally obvious if all your wrappers are of document type ProjectGroupPage or some are of other doctypes - if they are, you can match more than one type in a template:
/Chriztian
Thank you! That worked perfectly exactly how i wanted it to be :)
I would like to have only a certain number of the project items showing on the page i.e first 4/5 i have implemented this for blog feeds in razor but not xslt.
Thanks
Hi Molly,
Sorry I hadn't seen the question about limiting output.
To do that you can add a so-called "predicate" to the apply-templates instruction, e.g.:
There's a little caveat though - if you happen to need sorting the nodes, you'd normally just "open" the apply-templates and throw that in, like this:
But that will actually only sort the 5 selected nodes by date - when what you'd usually want was to get the 5 newest nodes.
For that you can use a for-each instruction:
Not that you're still using the generic template for the actual rendering, which is rather nice :-)
/Chriztian
Correction: That last line should read: "Note that you're still using ..."
/Chriztian
Thank you very much!
to extend this i would like to be able to add seperate styling to the first projectitem of each section. would i do this by adding another template and selecting the position of the 1st item and applying a class to it?
Thank you in advance. you are very helpful and i'm learning a lot by breaking it down.
Hi Molly,
To do that, one needs just a little information about how the position() function works - it returns the position of the "current node" within the "current node list" (neither of these have anything to do with Umbraco's $currentPage parameter). The current node is the ProjectItem element when you're inside that template; or if you're inside a for-each "loop" it's the current node in the loop.
The current node list is all the nodes selected by the select attribute in an apply-templates or a for-each instruction.
So, in your current scenario, if you were to print the value of position() within the for-each loop, you'd get 1, 2, 3, 4, 5, 6, 7 etc. - but if you print the value from within the ProjectItem template, you'll get 1, 1, 1, 1 etc. - because we only apply-templates to a single element (the current) in the for-each.
The way I usually handle this is by sending an index parameter into the template - like this:
This makes you keep the separate template - and it's easy to see what's happening.
/Chriztian
Ok, so i think i have everything in the right place. Here is my code now:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nbsp " ">
]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:autofolders.library="urn:autofolders.library" xmlns:uTube.XSLT="urn:uTube.XSLT"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets autofolders.library uTube.XSLT ">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:template match="/">
<!-- Process all ProjectGroupPage children of $currentPage -->
<xsl:apply-templates select="$currentPage/ProjectGroupPage" />
</xsl:template>
<xsl:template match="ProjectGroupPage">
<h1><xsl:value-of select="@nodeName" /></h1>
<!-- Process all ProjectItem children of this ProjectGroupPage -->
<xsl:apply-templates select="ProjectItem" />
<xsl:for-each select="ProjectItem">
<xsl:sort select="@createDate" data-type="text" order="descending" />
<xsl:if test="position() <= 4">
<xsl:apply-templates select=".">
<xsl:with-param name="index" select="$position()" />
</xsl:apply-templates>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="ProjectItem">
<xsl:param name="index" />
<div class="item">
<xsl:if test="$index =1">
<!--override the class attribute on first item -->
<xsl:attribute name="class">first item</xsl:attribute>
</xsl:if>
<a href="{umbraco.library:NiceUrl(@id)}">
<img id="project-home-image" src="/imageGen.ashx?image={RF-Slide/image}&width=172&height=166" alt="{title}" />
</a>
<a href="{umbraco.library:NiceUrl(@id)}">
<h2><xsl:value-of select="@nodeName"/></h2>
</a>
</div>
</xsl:template>
</xsl:stylesheet>
It gives me this error: (I looked into the with-param element and checked it seems to look fine. )
Error occured
Error in XSLT at line 33, char 11
31: <xsl:if test="position() <= 4">
32: <xsl:apply-templates select=".">
33: >>> <xsl:with-param name="index" select="$position()" /> <<<
34: </xsl:apply-templates>
35: </xsl:if>
Thanks Chriztian
Oh - my bad :-(
It should read like this (without the dollar sign):
Also, you should remove this line (just above the for-each):
since you're now using a for-each because of the sorting/limiting.
/Chriztian
Great, that's perfect! Thank you.
I resized the image for the first item through css, is it possible for me to apply the img for the first item within that code to have the imageGen on there to resize the first to the new size 280px wide? like it is underneath.
I figured out a way,
I set the height and width i wanted the images to be in the css and then in the xslt i set the imageGen width and height to 100% and it worked perfect :)
I want to specifically target each section within the xslt so that say i have 3 sections
Marketing
<projectItem> <projectItem> <projectItem> <projectItem> more..links to projectgrouppage = marketing
Creative
<projectItem> <projectItem> <projectItem> <projectItem> <more..links to projectgrouppage = creative>
Lead
<projectItem> <projectItem> <projectItem> <projectItem> <more..links to projectgrouppage = lead>
i want to add a small <more..> link on the end of each section to go to the parent page (ProjectGroupPage) of the (ProjectItems) within each section.
I'm guessing i could add this as another attribute after the initial attribute i already have linking to the parent ?
I actually also figured that out i added the attribute here :
<xsl:with-param name="index" select="position()" />
</xsl:apply-templates>
</xsl:if>
</xsl:for-each>
<a href="{umbraco.library:NiceUrl(@id)}"><img src="/media/8350/arrow.jpg" alt="More..." /></a>
</xsl:template>
<xsl:template match="ProjectItem">
Just after the closing of for-each and before closing of template and it worked perfect :)
Sorry to be a pain, but I'm spending quite some time trying to get 2 of those project items any it doesn't matter to display on the homepage. I can either get 2 of each projectgrouppage project items to display or all of the project items. It seems to ignore the sorting i put in there. This is what i have ..
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nbsp " ">
]>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:autofolders.library="urn:autofolders.library" xmlns:uTube.XSLT="urn:uTube.XSLT"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets autofolders.library uTube.XSLT ">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:template match="/">
<!-- start writing XSLT -->
<xsl:apply-templates select="$currentPage/OurWork/ProjectGroupPage" />
</xsl:template>
<xsl:template match="ProjectItem" >
<xsl:for-each select="ProjectItem">
<xsl:sort select="@createDate" data-type="text" order="descending" />
<xsl:if test="position() <= 2">
<xsl:apply-templates select="." />
</xsl:if>
</xsl:for-each>
<div class="home-item">
<a href="{umbraco.library:NiceUrl(@id)}">
<img id="project-home-image">
<xsl:attribute name="src">
<xsl:text>/imageGen.ashx?image=</xsl:text>
<xsl:value-of select="RF-Slide/image"/>
<xsl:text>&width=172&height=166</xsl:text>
</xsl:attribute>
<xsl:attribute name="alt">
<xsl:value-of select="projectTitle"/>
</xsl:attribute>
</img>
</a>
<a href="{umbraco.library:NiceUrl(@id)}">
<h2>
<!--<xsl:attribute name="title">
<xsl:value-of select="projectTitle"/>
</xsl:attribute>-->
<xsl:value-of select="@nodeName"/>
</h2>
</a>
<!-- <p><xsl:value-of select="projectSummary"/></p> -->
</div>
</xsl:template>
</xsl:stylesheet>
Hi Molly,
It just seems like you're a little confused about apply-templates, for-each and templates. No worries, you'll grok it eventually!
This is how you'd put it together - the comments should help you with what's going on:
Please note that just by extracting the long select from the for-each into a variable, that it's suddenly much easier to "read" what's going on, right?
/Chriztian
It's much easier looking at it like that! I seem to confuse myself and make it so complicated and lost! This helps alot so thank you!
Since then the projects to display on the homepage have changed slightly, as i was using a multinode picker then i swapped to the one above to come in automatically but now they want the treepicker back so they can select 4 items for the homepage with the first having its own style, I'm pretty sure i can implement the param for the class to the first item, i have had the multinode working when my project items were originally under one node <our work> under <homepage> but now i have child sections<projectgrouppage> under the <our work> and the project items are under each of those project group pages, i get the node to show all of those but for some reason they are all red and won't drag over.
This is what i have (it's strange because one minute it was only displaying the <our work > node and the next it was displaying the children underneat, so i'm unsure whether it's something i'm doing or it's basically just glitchy :S
<xsl:for-each select="$currentPage/ancestor-or-self::*/Homepage/projectsToDisplayOnHomepage/MultiNodePicker/nodeId">
<xsl:variable name="myPosition" select="position()"/>
<xsl:variable name="idProject" select="."/>
<xsl:for-each select="$currentPage/ancestor-or-self::*/Homepage/OurWork/ProjectGroupPage/ProjectItem">
<xsl:variable name="idNodePicker" select="@id"/>
<xsl:if test="$idNodePicker = $idProject">
<xsl:if test="$myPosition mod 4 = 0">
<xsl:text disable-output-escaping="yes"><![CDATA[<div class="item-row"]]></xsl:text>
<xsl:text disable-output-escaping="yes"><![CDATA[>]]></xsl:text>
</xsl:if>
This is my screenshot
I was wrong, it seems i am struggling with where to place the with-param function as i'm now looking at unfamiliar elements with the multinode picker, my bad!
is working on a reply...