SharePoint 2010 Add Paging to Custom DataViewWebPart - xslt

I have a custom DataViewWebPart to which I would like to add paging. I have included the meat of the XSLT I am using to form the scope of the details for my web part below. Any suggestions on how to implement paging on this item (since I am utilizing keys it's not so clear to me).
<xsl:key name="casebystate" match="Row" use="#StoreState"/>
<xsl:template match="/">
<xsl:variable name="cbs_Rows" select="/dsQueryResponse/Rows/Row/#StoreState"/>
<table border="0" width="100%" cellpadding="2" cellspacing="0">
<tr valign="top">
<th class="ms-vh" nowrap="nowrap">State</th>
<th class="ms-vh" nowrap="nowrap">Totals</th>
</tr>
<xsl:for-each select="//Row[generate-id() = generate-id(key('casebystate', #StoreState)[1])]">
<xsl:sort select="#StoreState"/>
<xsl:for-each select="key('casebystate', #StoreState)">
<xsl:call-template name="CaseByState.rowview" />
</xsl:for-each>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="CaseByState.rowview">
<xsl:variable name="cbs_NewSortValue" select="ddwrt:NameChanged(string(#StoreState), 0)"/>
<xsl:if test="string-length($cbs_NewSortValue) > 0">
<tr id="group0{generate-id()}">
<td class="abh-chrtStatTitle">
<xsl:value-of select="#StoreState"/>
</td>
<td class="abh-chrtStatValue">
<xsl:value-of select="count(key('casebystate', #StoreState))"></xsl:value-of>
</td>
</tr>
</xsl:if>
</xsl:template>
Thanks for the help in advance!

If you are using Sharepoint Designer 2010, are you able to select your dvwp in the designer, go to the data view tools section and click on paging from the options tab of the ribbon? That should allow you to specify paging for your webpart.

Related

XSL conditions: how to choose a value from list only when the condition is met?

I am trying to implement a logic which would do choosing a value if it matches the condition. The problem is, if my list contains more elements than one, it wouldn't display the value which matches the condition, but only the first one. Could you provide some advice? thanks. I need this to implement multiple filtering by conditions. Meaning that in this for-each I would display only books with year 2008, but in another table I could also use 2010 from the list of years. The below example is a part of the template for one table which uses 2008, but I will have multiple tables where I would filter books by 2010 as well.
XSL:
<xsl:for-each select="years">
<xsl:choose>
<xsl:when test="year=2008">
<td class="year"><xsl:value-of select="year"/></td>
</xsl:when>
</xsl:choose>
</xsl:for-each>
XML:
<book>
<title>Professional ASP.NET 4 in C# and VB</title>
<author>Bill Evjen, Scott Hanselman, Devin Rader</author>
<country>USA</country>
<price>580</price>
<years>
<year>2010</year>
<year>2008</year>
</years>
</book>
This one would populate 2010 into my HTML, not 2008 as I would expect. Is this doable at all?
Update:
I've tried this approach for example to filter against multiple conditions separately:
<xsl:for-each select="library/book">
<tr>
<td class="filterTd title"><xsl:value-of select="title"/></td>
<td class="filterTd author"><xsl:value-of select="author"/></td>
<td class="filterTd price"><xsl:value-of select="price"/></td>
<xsl:for-each select="years/year[.2008]">
<td class="year">
<xsl:value-of select="."/>
</td>
</xsl:for-each>
<xsl:for-each select="years/year[.2010]">
<td class="year">
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
This would not populate any td at all in my case..
If you only want to display books for a specific year, you would put a condition in the xsl:for-each that selects the book
<xsl:for-each select="library/book[years/year=$year]">
Where $year is a variable (or parameter) containing the year you want
Do note, if you know you are dealing with a specific year, then you don't actually need to do <xsl:value-of select="year" />, you can just output the year value you are working with.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<xsl:call-template name="table">
<xsl:with-param name="year" select="2008" />
</xsl:call-template>
</xsl:template>
<xsl:template name="table">
<xsl:param name="year" />
<table>
<xsl:for-each select="library/book[years/year=$year]">
<tr>
<td class="filterTd title"><xsl:value-of select="title"/></td>
<td class="filterTd author"><xsl:value-of select="author"/></td>
<td class="filterTd price"><xsl:value-of select="price"/></td>
<td class="year">
<xsl:value-of select="$year"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Note, you could also use a key here to look up books by year
<xsl:key name="booksByYear" match="book" use="years/year" />
Then, the xsl:for-each to select books, looks like this:
<xsl:for-each select="key('booksByYear', $year)">

Improved XSL script with a linked in XML file and applying templates

Here is some XSL script:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msa="http://www.publictalksoftware.co.uk/msa">
<xsl:output method="html" indent="yes" version="4.01"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
doctype-public="//W3C//DTD XHTML 1.0 Transitional//EN"/>
<xsl:variable name="DutyHistory" select="document('DutyAssignHistory.XML')"/>
<xsl:template match="/">
<html>
<head>
<title>Test</title>
</head>
<body>
<xsl:for-each select="MeetingWorkBook/Meeting">
<p>
<xsl:value-of select ="Date/#ThisWeek"/>
</p>
<xsl:variable name="Week" select="Date/#ThisWeek"/>
<table>
<tr>
<td>Sound</td>
<td>
<xsl:value-of select="$DutyHistory/msa:DutyAssignmentHistory/msa:DutyAssignments/msa:DutyAssignmentEntry[#Week=$Week and #Mode='Weekend']/msa:Assignment[#Index='1' and #IndexType='Fixed']"/>
</td>
</tr>
<tr>
<td>Platform</td>
<td>
<xsl:value-of select="$DutyHistory/msa:DutyAssignmentHistory/msa:DutyAssignments/msa:DutyAssignmentEntry[#Week=$Week and #Mode='Weekend']/msa:Assignment[#Index='5' and #IndexType='Fixed']"/>
</td>
</tr>
<tr>
<td>Left Mike</td>
<td>
<xsl:value-of select="$DutyHistory/msa:DutyAssignmentHistory/msa:DutyAssignments/msa:DutyAssignmentEntry[#Week=$Week and #Mode='Weekend']/msa:Assignment[#Index='7' and #IndexType='Fixed']"/>
</td>
</tr>
<tr>
<td>Right Mike</td>
<td>
<xsl:value-of select="$DutyHistory/msa:DutyAssignmentHistory/msa:DutyAssignments/msa:DutyAssignmentEntry[#Week=$Week and #Mode='Weekend']/msa:Assignment[#Index='8' and #IndexType='Fixed']"/>
</td>
</tr>
<tr>
<td>Public Talk Chairman</td>
<td>
<xsl:value-of select="$DutyHistory/msa:DutyAssignmentHistory/msa:DutyAssignments/msa:DutyAssignmentEntry[#Week=$Week and #Mode='Weekend']/msa:Assignment[#Index='4' and #IndexType='Custom']"/>
</td>
</tr>
<tr>
<td>Watchtower Reader</td>
<td>
<xsl:value-of select="$DutyHistory/msa:DutyAssignmentHistory/msa:DutyAssignments/msa:DutyAssignmentEntry[#Week=$Week and #Mode='Weekend']/msa:Assignment[#Index='5' and #IndexType='Custom']"/>
</td>
</tr>
</table>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
As you can see it is linking in another XML document for reference. Here is one example of that linked in file:
<?xml version="1.0" encoding="utf-8"?>
<DutyAssignmentHistory xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.publictalksoftware.co.uk/msa">
<DutyAssignments>
<DutyAssignmentEntry Date="2018-01-04" Week="W20180101" Template="0" Mode="Midweek">
<Assignment Index="2" IndexType="Fixed">Name 1</Assignment>
<Assignment Index="5" IndexType="Fixed">Name 2</Assignment>
<Assignment Index="7" IndexType="Fixed">Name 3</Assignment>
<Assignment Index="8" IndexType="Fixed">Name 4</Assignment>
<Assignment Index="13" IndexType="Fixed">Name 5</Assignment>
<Assignment Index="14" IndexType="Fixed">Name 6</Assignment>
</DutyAssignmentEntry>
</DutyAssignments>
</DutyAssignmentHistory>
Potentially a user might want to reference the information in the XML and display it however they like but I am wanting to show them the simplest method.
As you can see there are several criteria:
Week (WYYYYMMDD)
Mode (Midweek, Weekend or Weekly)
Template(0 or higher)
The above will filter to the right week of assignments. Then, to identify the actual assignment:
Index (numeric value)
IndexType (Fixed, CustomFixed or Custom)
Can I use templates in any way (perhaps with variables) to simplify the code as it is getting repetative?
You could use a template and pass parameters, or in XSLT 2.0 or higher you could also define a function, which makes it much easier to use and saves some typing. But for what you are currently doing, a variable and some predicate filters seems to be the most simple and easy.
The most simple and easy way would be to bind a variable with the weekend assignments, and then apply your predicate filter to select the one with the #Index and #IndexType:
<xsl:variable name="Week" select="Date/#ThisWeek"/>
<xsl:variable name="weekend-assignments"
select="$DutyHistory/msa:DutyAssignmentHistory/msa:DutyAssignments
/msa:DutyAssignmentEntry[#Week=$Week and #Mode='Weekend']/msa:Assignment"/>
<table>
<tr>
<td>Sound</td>
<td>
<xsl:value-of select="$weekend-assignments[#Index='1' and #IndexType='Fixed']"/>
</td>
</tr>
If you make the variable hold an unfiltered set of Assignment elements, you could perform all of the filtering in the predicates:
<xsl:variable name="Week" select="Date/#ThisWeek"/>
<xsl:variable name="assignments"
select="$DutyHistory/msa:DutyAssignmentHistory/msa:DutyAssignments
/msa:DutyAssignmentEntry/msa:Assignment"/>
<table>
<tr>
<td>Sound</td>
<td>
<xsl:value-of
select="$assignments[#Index='1' and #IndexType='Fixed']
[..[#Week=$Week and #Mode='Weekend' and #Template='0']]"/>
</td>
</tr>
If you want to consolidate the logic for generating the columns, you could define a template for msa:Assignment:
<xsl:template match="msa:Assignment">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
And then use it like this:
<table>
<tr>
<td>Sound</td>
<xsl:apply-templates select="$weekend-assignments[#Index='1' and #IndexType='Fixed']"/>
If you want to consolidate the logic for generating rows, you could define a template for msa:Assignment and send in a parameter for the first column:
<xsl:template match="msa:Assignment">
<xsl:param name="label"/>
<tr>
<td><xsl:value-of select="$label"/></td>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:template>
And then use it like this:
<table>
<xsl:apply-templates select="$weekend-assignments[#Index='1' and #IndexType='Fixed']">
<xsl:with-param name="label" select="'Sound'"/>
</xsl:apply-templates>

nested xsl:for-each does not work

I woud like to make some nested loops over my xml doc
Here is my xml
<evolutionlist>
<date id="22_05_2014">
<objet>
<identifier>1VD5-3452-8R5</identifier>
<link>Link1</link>
<title>EXCHANGE OF ELEMENTS</title>
</objet>
<objet>
<identifier>1V24-34A2-8C5</identifier>
<link>Link1</link>
<title>NEW ELEMENT</title>
</objet>
</date>
<date id="21_05_2014">
<identifier>1VV4-34A2-8C5</identifier>
<link>Link2</link>
<title>REPLACE</title>
</date>
</evolutionlist>
Ideally, I woudl like to display something like
22_05_2014
objet1 (with add infos)
objet2 (with add infos)
21_05_2014
objet3 (with add infos)
I made:
<xsl:for-each select="//date">
<xsl:value-of select="#id"/>
<xsl:for-each select="objet">
<tr>
<td>
<xsl:value-of select="identifier"/>
</td>
<td>
<xsl:value-of select="link"/>
</td>
<td>
<xsl:value-of select="title"/>
</td>
</tr>
</xsl:for-each>
</xsl:for-each>
but I got
22_05_2014 21_05_2014
objet1
objet2
objet3
where did I go wrong ?
EDIT
I tried
<xsl:for-each select="./objet">
for the second loop but that did not work either
Shame on me !
I forgot to do this:
<tr>
<td>
<xsl:value-of select="./#id"/>
</td>
</tr>
You were right, Ian Roberts, by encouraging me do write the real code

XSL Repeat Template Object containing List<Object>

I have a Library object that contains a collection of Books... The Library object has properties like Name, Address, Phone... While the Book object has properties like ISDN, Title, Author, and Price.
XML looks something like this...
<Library>
<Name>Metro Library</Name>
<Address>1 Post Rd. Brooklyn, NY 11218</Address>
<Phone>800 976-7070</Phone>
<Books>
<Book>
<ISDN>123456789</ISDN>
<Title>Fishing with Luke</Title>
<Author>Luke Miller</Author>
<Price>18.99</Price>
</Book>
<Book>
<ISDN>234567890</ISDN>
<Title>Hunting with Paul</Title>
<Author>Paul Worthington</Author>
<Price>28.99</Price>
</Book>
...
And more books
...
</Books>
</Library>
I have a template with space for only 10 per page for example. There can be hundreds of books in the list of Books... So I need to limit the number of books and repeat the template every 10 books.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<div>
<table>
<tr>
<td>NAME</td>
<td><xsl:value-of select="/Library/Name"/></td>
</tr>
<tr>
<td>ADDRESS</td>
<td><xsl:value-of select="/Library/Address"/></td>
</tr>
<tr>
<td>PHONE</td>
<td><xsl:value-of select="/Library/Phone"/></td>
</tr>
</table>
<table>
<xsl:for-each select="/Library/Books/Book">
<tr>
<td><xsl:value-of select="position()"/></td>
<td><xsl:value-of select="ISDN"/></td>
<td><xsl:value-of select="Title"/></td>
<td><xsl:value-of select="Author"/></td>
<td><xsl:value-of select="Price"/></td>
</tr>
</xsl:for-each>
</table>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
How can I get the Library information to appear on all repeating pages and add 10 books per page?... First page has Library info with Books 1 thru 10, Second page has Library info with Books 11 thru 20, and so on??
Thanks
For starters, try not to use for-each, apply-templates allows the engine to optimise the order that events are processed.
It appears that you are calling this stylesheet from some other system, so the approach I've taken is to define a pagination param. In the host language, when you call this just change the root parameter. This then allows you to select the require pages in this line here:
Books/Book[($page - 1)*10 < position() and position() <= ($page)*10]
This should do the trick.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="page" select="1"/>
<xsl:template match="/Library">
<html>
<body>
<div>
<table>
<tr>
<td>NAME</td>
<td>
<xsl:value-of select="/Name"/>
</td>
</tr>
<tr>
<td>ADDRESS</td>
<td>
<xsl:value-of select="/Address"/>
</td>
</tr>
<tr>
<td>PHONE</td>
<td>
<xsl:value-of select="/Phone"/>
</td>
</tr>
</table>
<table>
<xsl:apply-templates select="Books/Book[($page - 1)*10 < position() and position() <= ($page)*10]"/>
</table>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="/Book">
<tr>
<td>
<xsl:value-of select="position()"/>
</td>
<td>
<xsl:value-of select="ISDN"/>
</td>
<td>
<xsl:value-of select="Title"/>
</td>
<td>
<xsl:value-of select="Author"/>
</td>
<td>
<xsl:value-of select="Price"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>

How to repeat XSLT child nodes of the same name?

I have an xml dump of a transaction that I want to create a report from.
The (simplified) xml structure looks like:
Subject
id
name
license
event id=key
result
eventtype
date
action
charge id=key
result
code
date
...etc
There is only one id, name, and license for every subject; but there could be many events, and many charges for each subject--each one having a different result.
The report output is a table type, with each subject having a header of name, id, license, and multiple blocks of data transactions listed below the header. Each data block has different values and structure based on the type of transaction.
It works perfectly for the header, and one each of the data transaction types, but only the first event transaction and the first charge transaction will print out.
The (simplified) xsl looks like:
<xsl:for-each select="file/subject">
<xsl:if test="not (#subject = preceding-sibling::subject[1])">
<table width="754" border="2" cellpadding="0">
<tr>
<td width="172">ctn: <strong><xsl:value-of select="id"/></strong></td>
<td width="364">defendant: <strong><xsl:value-of select="name"/></strong></td>
<td width="200">sid: <strong><xsl:value-of select="license"/></strong></td>
</tr>
</table>
</xsl:if>
<blockquote>
<xsl:if test="event/eventType != ' '">
<table width="695" border="1">
<tr>
<td>Event<xsl:value-of select="event/result"/></td>
<td>Eventtype<xsl:value-of select="event/eventtype"/></td>
<td>Date<xsl:value-of select="event/date"/></td>
</tr>
</table>
</xsl:if>
<xsl:if test="charge/code != ' '">
<table width="695" border="1">
<tr>
<td>Charge<xsl:value-of select="charge/result"/></td>
<td>Code<xsl:value-of select="charge/code"/></td>
<td>Date<xsl:value-of select="charge/date"/></td>
</tr>
</table>
</xsl:if>
</blockquote>
</xsl:for-each>
I assume that I need an additional for-each to scan the child nodes, but adding the additional loop just gives me the headers.
Any thoughts?
(and thanks for taking the time to read this far. :o)
Where possible in XSLT, avoid for-each and apply your nodes to transformation templates instead.
I came up with this, which you can run at this playground session - not sure if it's broadly what you're after.
<!-- root and static content -->
<xsl:template match="/">
<xsl:apply-templates select='root/Subject' />
</xsl:template>
<!-- iteration content: subject -->
<xsl:template match='Subject'>
<table>
<tr>
<td>ctn: <strong><xsl:value-of select="id"/></strong></td>
<td>defendant: <strong><xsl:value-of select="name"/></strong></td>
<td>sid: <strong><xsl:value-of select="license"/></strong></td>
</tr>
</table>
<xsl:apply-templates select='event' />
<xsl:apply-templates select='charge' />
</xsl:template>
<!-- iteration content: event -->
<xsl:template match='event'>
<blockquote>
<table>
<tr>
<td>Event: <xsl:value-of select="result"/></td>
<td>Event type: <xsl:value-of select="eventtype"/></td>
<td>Date: <xsl:value-of select="date"/></td>
</tr>
</table>
</blockquote>
</xsl:template>
<!-- iteration content: charge -->
<xsl:template match='charge'>
<blockquote>
<table>
<tr>
<td>Charge: <xsl:value-of select="result"/></td>
<td>Code: <xsl:value-of select="code"/></td>
<td>Date: <xsl:value-of select="date"/></td>
</tr>
</table>
</blockquote>
</xsl:template>