[xsl] Getting a NodeList from a NodeList II (Full Problem Shown with Code)

Subject: [xsl] Getting a NodeList from a NodeList II (Full Problem Shown with Code)
From: "Allistair Crossley" <Allistair.Crossley@xxxxxxx>
Date: Wed, 2 Jul 2003 09:22:00 +0100
OK, I have taken some time below to explain in more detail what I am after now :)

THE XML
=======

The XML is essentially a list of employee nodes. Each employee has some information and also some date stamped approval information. It's not really relevant "what" this is to the problem, but it is holiday stuff. I also have some "filter" nodes under the root. This was my solution to passing Query String values to the XSL from the ASP page that transforms the XML with XSL. More later..

<employees>
  <filterDepartment value="All Staff" /> 
  <filterYear value="2003" /> 
  <filterThreshold value="0" /> 
  <employee username="johnsmith" fullname="John Smith" id="999" department="Sales">
    <approvals>
      <approval timestamp="1043884800000" date="30/01/2003" year="2003" type="F" requested="1043918289965" diff="-33489965" over4Wks="false" isRetro="true" /> 
      <approval timestamp="1043971200000" date="31/01/2003" year="2003" type="F" requested="1043918291578" diff="52908422" isRetro="false" over4Wks="false" />
    </approvals>
    <allocations>
      <allocation year="2003" days="23" /> 
      <allocation year="2004" days="24" /> 
    </allocations>
  </employee>
</employees>


THE XSL AT THE MOMENT
=====================

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). Now, I do not like using proprietry Microsoft behaviours ATALL, but I could not think how else..I suppose maybe a choose block but I have several reports that all use the same kind of technqiue so I put the function into a separate XSL which I import.

<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>

COMP:GETDEPARTMENTS(/) MSXML FUNCTION
=====================================

You will notice comp:getDepartments(/). This is a MSXML function. The reason I used this was because the XSL has to be aware of different filters that define what the report shows. So, my reporting front end passes in either All Staff, Some Staff or a Department name. Depending on what comes in, I have to change the node list that are iterated over. So I have this function which I pass the root node into and it looks a bit like this..

<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 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.

THE PROBLEM
===========

Remember how for each department I loop over the employees in it with;

  <xsl:for-each select="//employee[./@department = $deptName]">
    <xsl:sort select="@fullname" order="ascending" />

    <xsl:value-of select="./@fullname" />
  </xsl:for-each>

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. I want therefore to bring back "all employees in the current department where they have approvals whose timestamp value (a date in MS) falls between a from date filter filterFDate and to date filter filterTDate.

The way I thought about doing this was to send the whole list of employees in the department to a function like this..

  <xsl:for-each select="comp:getFilteredEmployees(//employee[./@department = $deptName])">
    <xsl:sort select="@fullname" order="ascending" />

    <xsl:value-of select="./@fullname" />
  </xsl:for-each>

BUT this comes into the function as a node list, and I cannot then apply another "select" type command on the node list to get another node list to return to the call.

MY CURRENT WORKAROUND (IS THIS THE SOLUTION?)
=============================================

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. 

UPCOMING PROBLEM
================

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??

FINALLY
=======

Sorry this is so long. I do hope to hear back from some of you on this!

Allistair.


<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