Subject: RE: [xsl] Getting a NodeList from a NodeList II (Full Problem Shown with Code) From: "Allistair Crossley" <Allistair.Crossley@xxxxxxx> Date: Wed, 2 Jul 2003 11:14:20 +0100 |
Thanks loads for looking through that for me Jeni. I will look at using the keys and XSL way of filtering department as you suggest. The one point I would make about using <= or <= is that it does not matter which one I use, both throw errors. The only way I got it to work was with using > and then notting it!! Cheers! -----Original Message----- From: Jeni Tennison [mailto:jeni@xxxxxxxxxxxxxxxx] Sent: 02 July 2003 11:05 To: Allistair Crossley Cc: xsl-list@xxxxxxxxxxxxxxxxxxxxxx Subject: Re: [xsl] Getting a NodeList from a NodeList II (Full Problem Shown with Code) Hi Allistair, > The report I am trying to generate is based on department, so I loop > over unique departments and for each one I get the employees in it > and print some information. I use a msxml function getDepartments > which is my way of getting a different set of departments depending > on the filterDepartment node value (which was a Query String > parameter). [snip] > <xsl:for-each select="comp:getDepartments(/)"> > <xsl:sort select="." order="ascending" /> > <xsl:variable name="deptName" select="." /> > <xsl:for-each select="//employee[./@department = $deptName]"> > <xsl:sort select="@fullname" order="ascending" /> > <xsl:value-of select="./@fullname" /> > .. other code > </xsl:for-each> > </xsl:for-each> [snip] > <msxsl:script language="JScript" implements-prefix="comp"> > function getDepartments(nodeList) { > var dept = (nodeList.item(0).selectSingleNode("//filterDepartment")).getAttribute("value"); > if(dept == "All Staff") { > return nodeList.item(0).selectNodes("//employee/@department[not(. = preceding::employee/@department)]"); > } else if(dept == "Some Staff") { > return nodeList.item(0).selectNodes("//employee/@department[not(. = preceding::employee/@department) and (" + > "(. = 'Sales') " + > "or (. = 'I.T'))]"); > } > return nodeList.item(0).selectNodes("//employee/@department[not(. = preceding::employee/@department) and (. = '" + dept + "')]"); > } > </msxml:script> I can see why you're having trouble here, but you can do this in XSLT. Obviously you can select the department that you're filtering by easily enough: <xsl:variable name="dept" select="/employees/filterDepartment/@value" /> And you can create a selection of the first employee in each department, used as a basis for the future filtering, in the way you are (or you could use keys to get this set, but one thing at a time): <xsl:variable name="employees" select="/employees/employee [not(@department = preceding-sibling::employee/@department)]" /> Now comes the part where you need to filter $employees based on $dept. You can do this with a predicate, with a test that returns true if you want a particular employee and false otherwise. If $dept is 'All Staff', you don't need any additional filtering, you want the employee: $employees[$dept = 'All Staff' ...] If $dept is 'Some Staff' then you want the employee if their @department is 'Sales' or 'I.T': $employees[$dept = 'All Staff' or ($dept = 'Some Staff' and (@department = 'Sales' or @department = 'I.T')) ...] Otherwise, you want the employee if their department is the same as $dept: $employees[$dept = 'All Staff' or ($dept = 'Some Staff' and (@department = 'Sales' or @department = 'I.T')) or @department = $dept] (This is very fiddly, I know. In XPath 2.0, there are conditional expressions, so you can do: if ($dept = 'All Staff') then $employees else if ($dept = 'Some Staff') then $employees[@department = 'Sales' or @department = 'I.T'] else $employees[@department = $dept] which is a lot more intuitive.) So you can use: <xsl:variable name="dept" select="/employees/filterDepartment/@value" /> <xsl:variable name="employees" select="/employees/employee [not(@department = preceding-sibling::employee/@department)]" /> <xsl:for-each select="$employees[$dept = 'All Staff' or ($dept = 'Some Staff' and (@department = 'Sales' or @department = 'I.T')) or @department = $dept]"> ... </xsl:for-each> instead of your current MSXML-specific function. By the way, the above is selecting <employee> elements rather than department attributes, so you need to change the code inside the <xsl:for-each> a little. I'd really recommend using keys for selecting the <employee> elements that belong to the same department as the employee you're processing. Use a key that indexes all the <employee> elements by their department: <xsl:key name="employees" match="employee" use="@department" /> and then select the employees using the key() function: key('employees', $deptName) In other words, you <xsl:for-each> should look like: <xsl:for-each select="$employees[$dept = 'All Staff' or ($dept = 'Some Staff' and (@department = 'Sales' or @department = 'I.T')) or @department = $dept]"> <xsl:sort select="@department" order="ascending" /> <xsl:variable name="deptName" select="@department" /> <xsl:for-each select="key('employees', $deptName)"> <xsl:sort select="@fullname" order="ascending" /> <xsl:value-of select="@fullname" /> ... </xsl:for-each> </xsl:for-each> > I could not think of another way to make my XSL "aware" of Query > String parameters other than to add them as top level "filter" nodes > with value attributes and then use a function like this. That's fine, and it works, but you might also consider using XSLT parameters to pass in these kinds of filters. > Well, what I want to do is filter the employees brought back > depending on some time filters I have which define a date range to > search within. [snip] > I thought about it differently after posting the message and decided > to put the whole condition that I would have done in the function > into the select so that I selected all emps in the current dept. > that had 1 or more approvals whose timestamp falls between the 2 > filters. > > <xsl:for-each select="//employee[(./@department = $deptName) and > (./approvals/approval [(@timestamp >= $filterFDate) and not > (@timestamp > $filterTDate)])]"> > <xsl:sort select="@fullname" order="ascending" /> > <xsl:value-of select="./@fullname" /> > </xsl:for-each> > > This seems to work fine in actual fact...but, as this is my first > outing with XSL I would really appreciate some constructive > criticism about my problem and how I have attempted to solve it and > if I could have done things better. That looks great. It's how I would have done it. Note that with the key suggestion above, it should look like: <xsl:for-each select="key('employees', $deptName) [approvals/approval[@timestamp >= $filterFDate and @timestamp <= $filterTDate]]"> ... </xsl:for-each> I've used <= rather than not(... > ...) in the above just because it makes more sense to me. > And shortly, I shall be having to change the report so that you can > order by not only department but employee name too. At the moment > the only way I can think of doing that is to create a whole separate > XSL sheet for it?? There's rarely a requirement to use a whole separate stylesheet. At the very least you can do something along the lines of: <xsl:choose> <xsl:when test="$orderBy = 'department'"> ... code that does ordering by department ... </xsl:when> <xsl:when test="$orderBy = 'name'"> ... code that does ordering by name ... </xsl:when> </xsl:choose> Let us know if you have any problems/questions about the above. If you want to know about how to use keys to select the employees with unique departments, have a look at http://www.jenitennison.com/xslt/grouping/muenchian.html. Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/ <FONT SIZE=1 FACE="VERDANA,ARIAL" COLOR=BLUE> ------------------------------------------------------- QAS Ltd. Developers of QuickAddress Software <a href="http://www.qas.com">www.qas.com</a> Registered in England: No 2582055 Registered in Australia: No 082 851 474 ------------------------------------------------------- </FONT> XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
RE: [xsl] msxml xpath problem, Jim Fuller | Thread | Re: [xsl] Getting a NodeList from a, Jeni Tennison |
RE: [xsl] msxml xpath problem, Jim Fuller | Date | [xsl] Parsing xsl:variable into HTM, jan . soer |
Month |