SharePoint 2013: Generate dynamic content using XSLT and a comma-delimited string

In SharePoint, the Content Query Web Part uses XSLT templates to display the results of each query. This is normally a fairly straightforward process (for XSLT). For an example, see the “Promo boxes” demo I wrote a while back.

There’s one area, though, where SharePoint lists and XSLT collide: Checkbox columns.

If you have a column in SharePoint that allows multiple choices, when you grab that column data in XSLT, SharePoint sends the data in a string that looks like this:

;#Choice1;#Choice2;#Choice3;#

Just to display that in a human-friendly way, you would have to run it through the translate function at the very least. But what if you want to render customized content based on each of those choices? SharePoint only supports XSLT 1.0, and there is nothing built into 1.0 that automatically splits a string into a traversible array.

In this post, I’ll provide a sample use case that does just that.

New Contracts rollup

The web part we’re going to build displays a list of recently-won bids or contracts. It includes the customer, the contract name, the contract amount, and the department in your company that won the contract. The hitch? A contract may be shared by two or more departments, and we want to credit them all.

For this we will need:

  1. A SharePoint list
  2. An XSLT template to display the results;
  3. A CQWP that points to the list;
  4. Some CSS to style the results

The list

Create a new SharePoint list named “New Contracts”. Give it the following columns:

  1. Title: Contract name
  2. AccountName: “Single Line of Text” column; The customer with whom the contract was signed.
  3. ContractAmount: “Currency” column; the amount of the contract.
  4. WinningDepartments: “Choice” column. Set the choices to whatever makes sense (We’ll use “UX”, “AppDev” and “ContactCenter” for this post). Choose “Display choices using: Checkboxes”.

As an aside, I always make my column names and “Choice” options one word, in camel case, with no special characters. This simplifies things when you’re configuring the CQWP and writing the XSLT. If you have a space in a column name, for instance, SharePoint renders the space as “_x0020_ ” in its internal list. Special characters get encoded in non-obvious ways, too. Avoid them whenever you can.

The XSLT

In SharePoint Designer, go to All Files -> Style LIbrary -> XSL Style Sheets, and open ItemStyle.xsl. Paste the following templates at the bottom of the file, just before the closing </xsl:stylesheet> tag.

    <!-- NewContracts -->
    <xsl:template name="NewContracts" match="Row[@Style='NewContracts']" mode="itemstyle">
        <xsl:variable name="DisplayTitle">
            <xsl:call-template name="OuterTemplate.GetTitle">
                <xsl:with-param name="Title" select="@Title"/>
                <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
            </xsl:call-template>
        </xsl:variable>

        <xsl:variable name="ContractAmount" select="@ContractAmount"/>
        <xsl:variable name="Departments" select="translate(@Departments,';#',',')"/>

            <div class="win-item">
                <div class="win-icons">
                
                    <xsl:call-template name="renderDepartmentIcons">
                        <xsl:with-param name="text" select="$Departments"/>
                    </xsl:call-template>
            
                </div>
                <div class="win-content">
                    <h4>
                           <xsl:value-of select="@AccountName"/>
                    </h4>
                    <p><xsl:value-of select="$DisplayTitle"/><span class="contract-amount">$<xsl:value-of select="format-number($ContractAmount,'###,###,###')"/></span></p>
                </div>
            </div>
    </xsl:template>    
    <!-- NewContracts -->

    <!-- DepartmentIcon render -->
    <xsl:template match="string/text()" name="renderDepartmentIcons">
        <xsl:param name="text" select="."/>
        <xsl:param name="sep" select="','"/>
        <xsl:choose>
            <xsl:when test="contains($text, $sep)">
                <xsl:variable name="iconName" select="normalize-space(substring-before($text, $sep))"/>
                <xsl:if test="string-length($iconName) &gt; 0">
                    <span class="win-icon {$iconName}">.</span>
                </xsl:if>
                <xsl:call-template name="renderDepartmentIcons">
                    <xsl:with-param name="text" select="substring-after($text, $sep)"/>
                </xsl:call-template>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <!-- DepartmentIcon render -->

Let’s go through it in detail.

    <!-- NewContracts -->
    <xsl:template name="NewContracts" match="Row[@Style='NewContracts']" mode="itemstyle">
        <xsl:variable name="DisplayTitle">
            <xsl:call-template name="OuterTemplate.GetTitle">
                <xsl:with-param name="Title" select="@Title"/>
                <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
            </xsl:call-template>
        </xsl:variable>

        <xsl:variable name="ContractAmount" select="@ContractAmount"/>
        <xsl:variable name="Departments" select="translate(@Departments,';#',',')"/>

We have a template called “NewContracts”, configured to show up as a selectable template in a SharePoint CQWP. The first thing we do is define three variables. “Title” and “ContractAmount” are straightforward.

“Departments” is the choice list. As I mention above, it arrives as a string with the choices separated by “;#”. So we run it through the translate function to turn those “;#” into commas. This makes them more reader-friendly if we just want to display the list, as well as making them ready for parsing into separate choices.

            <div class="win-item">
                <div class="win-icons">
                
                    <xsl:call-template name="renderDepartmentIcons">
                        <xsl:with-param name="text" select="$Departments"/>
                    </xsl:call-template>
            
                </div>
                <div class="win-content">
                    <h4>
                           <xsl:value-of select="@AccountName"/>
                    </h4>
                    <p><xsl:value-of select="$DisplayTitle"/><span class="contract-amount">$<xsl:value-of select="format-number($ContractAmount,'###,###,###')"/></span></p>
                </div>
            </div>
    </xsl:template>    
    <!-- NewContracts -->

Here is the actual markup for each item. Each item is divided into two parts: A “win-icons” div that will display an icon for each Department associated with the contract, and a “win-content” div that will display information about the contract: the client, contract name, and contract amount. Note that we’re using the format-number function to display the contract amount in traditional U.S. currency style.

You’ll notice that inside the “win-icons” div, we’re calling a template named renderDepartmentIcons, and passing it the (now comma-delimited) Departments string. That external template is what parses the string and renders content based on each value. So let’s take a look at it now:

    <!-- DepartmentIcon render -->
    <xsl:template match="string/text()" name="renderDepartmentIcons">
        <xsl:param name="text" select="."/>
        <xsl:param name="sep" select="','"/>
        <xsl:choose>
            <xsl:when test="contains($text, $sep)">
                <xsl:variable name="iconName" select="normalize-space(substring-before($text, $sep))"/>
                <xsl:if test="string-length($iconName) &gt; 0">
                    <span class="win-icon {$iconName}">.</span>
                </xsl:if>
                <xsl:call-template name="renderDepartmentIcons">
                    <xsl:with-param name="text" select="substring-after($text, $sep)"/>
                </xsl:call-template>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <!-- DepartmentIcon render -->

The template takes the passed list of Departments and assigns it to the parameter “text”. It then defines the parameter “sep” as a comma. In other words, it’s a function designed to handle a comma-delimited string, which is why we converted the Departments string to use commas before passing it along.

Because the template can only handle comma-delimited strings, it tests to see if the string contains a comma, using the contains function.

If the string contains a comma, the template grabs the part of the string before the first comma (using the substring-before function) and assigns it to a variable called “iconName”.

It then tests iconName to make sure iconName isn’t blank. If iconName passes the test, the template renders out a span of class “win-icon”, with the iconName value as an additional class.

Regardless of whether a span was rendered or not, we call the renderDepartmentsIcons template again — but only pass it the part of the Department string AFTER the first comma, using the substring-after function. In other words, we chop off the part of the string we just evaluated. This loop continues until there are no more commas in the passed string.

Thus, if there is a single Department involved in the contract, the template will render out a single span, like this:

<span class="win-icon AppDev">.</span>

But if we have multiple departments, the template will render out multiple spans, like this:

<span class="win-icon AppDev">.</span>
<span class="win-icon UX">.</span>
<span class="win-icon ContactCenter">.</span>

There’s a period inside each span because otherwise SharePoint likes to lop off the closing </span> tag, resulting in broken markup.

The HTML

The above XSLT will generate HTML that looks like this for each item:

            <div class="win-item">
                <div class="win-icons">
                    <span class="win-icon AppDev">.</span>
                    <span class="win-icon UX">.</span>
                </div>
                <div class="win-content">
                    <h4>ABC Financial</h4>
                    <p>Really cool web application<span class="contract-amount">$98,800</p>
                </div>
            </div>

The CSS

Finally, we need a little CSS to tie it all together. Assume you’ve created a set of icons that each 25×25 pixels. Then the following CSS will stack those icons up on the left side of each item:

.win-icons {
    float:left;
    width:25px;
}
.win-content {
    margin-left:40px;
}
.win-icon {
    display:block;
    width:25px;
    height:25px;
    margin-bottom:5px;
    color:transparent;
    background: url(images/win-icons/default.png) top left no-repeat transparent;
}
.win-icon.AppDev {
    background-image: url(images/win-icons/AppDev.png);
}
.win-icon.UX {
    background-image: url(images/win-icons/UX.png);
}
.win-icon.ContactCenter {
    background-image: url(images/win-icons/ContactCenter.png);
}

Note that we set the color of the “win-icon” spans to “transparent” so that you can’t see the period we used to keep the <span> from collapsing.

The CQWP

Finally, place a Content Query Web Part on your page, point it at the “New Contracts” list, and choose the “NewContracts” template from the “Item Template” dropdown.

Categories: App Development.