Keys in For-Each
This is an open discussion with 9 replies, filed under General.
Search
I also tried this as an alternative:
<xsl:for-each select="../../projects/entry[service/item/@handle = ../../service/@handle]">
that didn’t work, but hardcoding in ‘ecommerce’ like so:
<xsl:for-each select="../../projects/entry[service/item/@handle = 'ecommerce']">
does work.
Any ideas what I am missing here with my XPath?
Can you post your full XML please (maybe to gist or pastie).
Sure thing Nick, it’s a bit of a beast. :)
I’m only guessing here since the logic is a little off-base, but I think this is what you’re looking for:
Firstly, the best way to think about xsl:key
is to think of them as a way to get an XML snippet (or formally, node-set
) of the items that you want, based on a value.
So the question you want to ask is:
Question: What do I want as my result set?
Answer: I want to get a list of projects. /data/projects/entry
Question: What value should I use to pluck the results I want out from my original XML?
Answer: I want to only get projects with a particular type of service. service/item/@handle
With the above answered, you can now construct your xsl:key
instruction:
<xsl:key name="service-handle" match="/data/projects/entry" use="service/item/@handle"/>
Next, you need to use the key to iterate through the node-set
. It’s best to think of the XML snippet you get back from the xsl:key
as any regular XML:
<xsl:for-each select="key('service-handle', service/@handle)"> ... </xsl:for-each>
So to clarify, something like: key('service-handle', 'ecommerce')
will result in a node-set of your e-commerce project
entries.
Putting it together:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="html" version="1.0" encoding="utf-8" indent="no"/> <xsl:key name="service-handle" match="/data/projects/entry" use="service/item/@handle"/> <xsl:variable name="service" select="''"/> <xsl:variable name="root" select="''"/> <xsl:variable name="root-page" select="''"/> <!-- Template Matches for Projects & Services main page --> <xsl:template match="services"> <xsl:apply-templates select="entry[../section/@handle = 'services'][premium = 'Yes']"/> <h3>Other Services</h3> <xsl:choose> <xsl:when test="$service = ''"> <ul class="stars"> <xsl:for-each select="entry[../section/@handle = 'services'][premium = 'No']"> <li> <a href="{$root}/{$root-page}/{service/@handle}"> <xsl:value-of select="service"/> </a> </li> </xsl:for-each> </ul> </xsl:when> <xsl:otherwise> <ul class="stars"> <xsl:for-each select="entry[../section/@handle = 'services'][service/@handle != $service]"> <li> <a href="{$root}/{$root-page}/{service/@handle}"> <xsl:value-of select="service"/> </a> </li> </xsl:for-each> </ul> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="entry[../section/@handle = 'services'][premium = 'Yes']"> <xsl:choose> <xsl:when test="$service = ''"> <div class="service" id="{service/@handle}"> <h2> <a href="{$root}/{$root-page}/{service/@handle}"> <xsl:value-of select="service"/> </a> </h2> <div class="grid_4 alpha"> <xsl:apply-templates select="shot"/> </div> <div class="grid_4"> <h3>Recent Projects</h3> <ul class="stars"> <xsl:for-each select="key('service-handle', service/@handle)"> <li> <a href="{$root}/projects-services/{project/@handle}/"> <xsl:value-of select="project"/> </a> </li> </xsl:for-each> </ul> </div> <div class="grid_4 omega description"> <xsl:copy-of select="description/*"/> <xsl:apply-templates select="partner-logo"/> </div> </div> </xsl:when> <xsl:otherwise> <xsl:for-each select="service[@handle = $service]"> <h2> <xsl:value-of select="."/> </h2> <xsl:copy-of select="../description/*"/> <xsl:apply-templates select="../partner-logo"/> <xsl:apply-templates select="../shot"/> </xsl:for-each> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
Note that there are many places where the XSLT code can be more elegantly handled.
Allen,
I see what your doing there. Ok, I will give all that a try.
Hopefully I can understand and learn. I suppose there are many ways to achieve something, it’s just how you approach the problem.
Thanks very much,
Can I ask why you set the Key use as:
service/item/@handle
but when you use the key function you use:
service/@handle
For reference, here’s the repasted XML source. I’ve removed elements that was irrelevant to this discussion: http://pastie.org/1114409.
This is because, xsl:key
is referencing the projects
element. You can see that the path needs to be (in full): /data/projects/entry/service/item/@handle
. This is setting up the project’s “identifier”.
With the key
function, it is grabbing the value that sits inside the services
entry, the path being (in ful): /data/services/entry/service/@handle
. This is telling xsl:key
what the identifier’s value should be.
Ok sorry about the delay in replying, been out of the office and unable to sit down and read this. Doing so now.
Right I’m kind of still a little lost here.
if the xsl:key uses /data/projects/entry as it’s match how does that link to the key function if that key function is referencing data/services/entry/service/@handle
I think I need to understand this in the simplest terms. I have been over to Jeni’s XSLT Pages, and tried to understand.
From what I understand the key() function takes 2 arguments, the name which I presume is merely a label and could be anything, the 2nd being the value which in this case is:
service/@handle
So I don’t understand what the match argument does in the xsl:key declaration? Because I am now presuming that service/@handle is in relation to the template match which is:
<xsl:template match="entry[../section/@handle = 'services'][premium = 'Yes']">
Which in turn is in:
<xsl:template match="services">
which obviously sits inside data
So I think basically my question is, what is the purpose of match argument in the initial declaration?
Actually I think the penny has dropped finally.
In the original key declaration we are setting up the matched element /data/projects/entry
and then when we call the key() function we are using /data/projects/entry
as the context.
So if I am in the services/entry
node my key is saying go to /data/projects/entry
and then by using the service/@handle
as the identifier, which say in my case may be eCommerce and then because my for-each
is in /data/projects/entry
it returns all the data that is in that node but also matches eCommerce too.
Is that right? Please tell me it is. :)
Create an account or sign in to comment.
Hi,
I’ve ran into a spot of bother with my XML and XPath. I’m trying to display my list of services, and with that my projects that relate to that particular service.
This is my site:
Now as you can see it’s just displaying Recent Projects and nothing within them.
Here is my XSLT that I have written.
Now I have tried to use the method and try something new and advanced so that I can learn different techniques to display my data, but it seems it’s not displaying what I want it to.
I’m thinking my problem is to do with the context of the node in the key function, but then again I could be doing this the completely wrong way. In my head I know what I want to happen, I want to display the project that’s service matches the service the list is being displayed in.
Anybody who can lend a hand I would be very appreciative and hopefully I can return the favour.