Sorting in a DVWP by a Column in Another List

This is something that I’ve tried to solve many times and each time I’ve ended up doing something more complicated than I’ve needed to, but I knew would work.  I finally bit the bullet and solved it the right way.

Here’s the scenario:

  • You have a primary list with many items in it
  • One of the columns in this list is a lookup column into another list
  • The second list also has a column called SortOrder, which contains a number indicating where in the sort order the lookup value ought to be displayed
  • When you display the items from the first list, you want to sort them by the corresponding SortOrder value in the second list

Let’s run through the scenario with these two lists:

Primary List

Title Color
Title A Green
Title B Green
Title C Yellow
Title D Red

Secondary List

Color SortOrder
Red 1
Yellow 2
Green 3

In your DWVP, you’ll have a template to gather the right rows from the primary list, then an xsl:for-each where you will do the sort.  The xsl:sort needs to look something like this:

<xsl:sort select=”/dsQueryResponse/secondary_list/Rows/Row[@Color = current()/@Color]/@SortOrder”/>

Assuming that you have a secondary sort of the Title column (<xsl:sort select=”@Title”/>), the results will be returned like this:

  • Title D
  • Title C
  • Title A
  • Title B

Simple little line, but it was a dickens to figure out the exact syntax.  Here’s how it breaks down:

The /dsQueryResponse/secondary_list/Rows/Row says to go and get the rows from the secondary list.  Anything within brackets filters that result set, so with [@Color = current()/@Color] we’re saying rows where the Color in the secondary_list equals the Color column in the current result set (the result set from the primary_list). Finally, the /@SortOrder grabs the value in the SortOrder column.

For all of this to work properly, you need to know that your data model will support this – you want to be sure that there is only one row per Color in the secondary_list.  You also have to have your DVWP set up with an AggregateDataSource containing both lists (in this case, they are named primary_list and secondary_list in the DataSource).

Similar Posts

11 Comments

  1. Dan:

    Sorry; I’ve been meaning to get back to you on both fronts. You can go about this several ways, but probably the most straightforward, based on the example above, would be to put an xsl:if into your dvt_1.details template like:

    <xsl:if test="/dsQueryResponse/secondary_list/Rows/Row[@Color = current()/@Color]/@SortOrder = $DesiredValue"/>
    ...do stuff...
    </xsl:if>

    M.

  2. no need to apologize – I really appreciate the assistance. my projects always seem to demand more than i know and provide little time to learn it. Any chance you can provide a simple example of … do stuff …? Also, in my never-ending search for stuff I don’t know how to do I also found the layout options and turned on the group/filtering options for the dvwp. With those I was exploring the option of letting the user pick the filter for the secondary list. out of the box, the interface doesn’t provide any filter choices for a column in the secondary list – any tips if this is possible and pointers if so? seems i need to learn xsl – any good books?

    1. Dan:

      The “do stuff” would basically be whatever you already have in your dvt_1.detail template. You’d just be wrapping that in the xsl:if, which in effect filters out the items which don’t meet your criteria.

      As for adding a way for your users to select a filter for the secondary list, you once again have some options. Can you explain a little about what the content of the primary/secondary lists is? I would initially suggest a dropdown (built in a DVWP with the secondary list as the data source) to offer options from the secondary list, but it all depends on what your content looks like and what you want the user experience to be.

      M.

  3. Essentially I have a document library “CPDDocuments” in a “Records” site, and a “Projects” list in a “CPDProjects” site. The “Projects” list has 2 columns “Title” and “ProjectStatus”.
    The “CPDDocuments” library has a “ProjectNo” column which equates to the “Title” column in the “Projects” list. The values for “ProjectStatus” are “Active” and “Inactive”. The “CPDDocuments” library contains several thousand documents and the client has asked for a view such that they only see documents for “Active” projects. I initially thought of just moving the “Inactive” documents to another library, but on further clarification the client wants an “interactive” solution which takes effect when they change the project status back and forth from “Active” to “Inactive”. I started going down the path of creating an event handler to move the documents back and forth between and active and an inactive library when it dawned on me that maybe I could just use a view (so simple in a database), and so began my journey into the world of sharepoint listviews and eventually the DVWP. I have found hints that it is possible and posts that say it isn’t, but from posts like yours I still think it is possible, I just don’t know how and the learning curve is steep. thanks for any assistance you can provide.

    1. Sorry again, Dan; you’d already explained this structure somewhere. Since it’s binary (Active/Inactive), I’d use two radio buttons which, when clicked, would call the same page with ?ProjectStatus=Active (or Inactive) on the Query String. Then, in the DVWP, do the following:

      * In the ParameterBindings section, add this line:

      <ParameterBinding Name="ProjectStatus" Location="QueryString(ProjectStatus)" DefaultValue=""/>

      (You can set a default if you want.)
      * Then, after this line:

      <xsl:output method="html" indent="no"/>

      add the line:

      <xsl:param name="ProjectStatus"/>

      Your xsl:if can then use that Query String value in its filter:

      <xsl:if test="/dsQueryResponse/CPDProjects/Rows/Row[@Title = current()/@ProjectNo]/@ProjectStatus = $ProjectStatus"/>
      ...do stuff...
      </xsl:if>

      M.

  4. thanks Mark. As i was pondering where to put the xsl:if, it occurred to me that the nature of the join means that I have already retrieved all of the CPDDocuments by the time I want to do the test, so my option is to just show only the records I want. With that in mind I put the xsl:if near the top of the xsl:for-each loop in the dvt_1.body template so that only rows that meet the test condition are processed (and viewed). It seems to work but was wondering what you think and if I am missing something.

  5. Hello Marc. Thanks for the info above. The filtering solution you provided above works if placed in the dvt_1.rowview. However, a snag is hit when you have do things like dvt_rowcount, first and last rows etc (for paged data viewing). For example:

    I have a listA containing a lookup column “companynameA” (multiple values) and listB containing “companynameB” column with all the company names and “locale” column containing each company’s location. What I want to do is filter listA based on a “locale”. Do you know of a way to do the filtering in one line? This is in SPD.

    Used:

    This basically brings back 0 rows from listA.

    1. Sorry, David. I thought I had already answered you, but I see I haven’t.

      You can add a filter on the rowset selection, something like this:

      <xsl:variable name="Rows" select="/dsQueryResponse/ListA/Rows/Row[/dsQueryResponse/ListB/Rows/Row[@CompanyName = current()/@CompanyName]/@Locale = 'US']"/>
      

      The ‘US’ value can also come from a parameter, so:

      <xsl:variable name="Rows" select="/dsQueryResponse/ListA/Rows/Row[/dsQueryResponse/ListB/Rows/Row[@CompanyName = current()/@CompanyName]/@Locale = $Locale]"/>
      

      Hope this helps…

      M.

Leave a Reply to David Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.