SharePoint Knowledge Base

Feb 09
Unlocking the Best Kept Secret in SharePoint Branding

As SharePoint consultants we do a lot of branding work and we haven't found anything nearly as powerful as what you can do with the client-side object model. This post explains a bit of the details to get you started with SharePoint customization and unlock what we consider to be the best-kept secret in SharePoint branding. As the SharePoint framework SPfx evolves Microsoft is recommending client-side rendering as the future development path.

SharePoint JavaScript object model (JSOM)

JSOM doesn't change the way SharePoint works, but instead simply inserts a little bit of code so that how it displays on the page looks different. CSR (client-side rendering) is the trick that makes this happen. As you can see from the three web parts below we can influence the behavior of the display of calendars, news, and individual items with the list.

Objects that have a JSLink property:

  • List Views (some exceptions, e.g. Calendar View)
  • List Forms (New, Edit, View forms)
  • List View and List Form web parts
  • Site Column (Fields)
  • Content Type

Screen shot of several lists all styled to look similar.

 

This happens through the magic of adding in a script file

 

You do that within a webpart's miscellaneous settings. Yes, it's the very last configuration setting. JSLink files have the ability to quickly and easily change how a list looks and how forms are rendered.

More specifically how the fields in that list should be displayed. Notice the read more button and image display in the screen shot below.

Programmatically setting the JSLink Property

We can define/change the JSLink property in many, many ways, as always SharePoint provides different paths to do the same thing:

  • Declaratively in Schema.xml file when building a List Definition
  • Programmatically using C# and the Server-Side Object Model
  • Programmatically using JavaScript using the JavaScript Object Model (JSOM)
  • Programmatically using .NET Client Side Object Model (CSOM)
  • Using PowerShell (in combination with CSOM)
  • Thru a SharePoint API REST Call

 

We won't enter in details of each method, as it could take pages and pages of information and examples, but it's easy to find the information on the Internet!

When building a List Definition (declaratively in XML), we can define the JSLink property for each Form (New, Edit, Display) and List View, right on the Schema.xml file.

This way, every time we create a List or Document Library, based on that specific List Definition, our custom rendering will be applied by default. No need to edit web part properties!

Take a look at this example, defining a custom rendering for NewForm.aspx form, using Visual Studio:

<List xmlns:ows="Microsoft SharePoint" Title="Requests" DisableGridEditing="TRUE" FolderCreation="FALSE" NavigateForFormsPages="FALSE" EnableContentTypes="TRUE" Direction="$Resources:Direction;" Url="Lists/Requests" BaseType="0" xmlns="http://schemas.microsoft.com/sharepoint/">

<MetaData>

<ContentTypes>

//Content types

</ContentTypes>

<Fields>

//Fields

</Fields>

<Views>

//Views

</Views>

<Forms>

<Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

<Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

<Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" JSLink="~site/Lists/Files/RequestFormCRS.js" />

</Forms>

</MetaData>

</List>

 

This is another example, of a JSLink applied to a View, also defined in Schema.xml:

 

JSLink URLs and Tokens

When you are constructing your JSLink URL there are a number of tokens you can take advantage of, they're there to avoid specifying long fully qualified URLs and/or domain names.

  • ~site – reference to the current SharePoint site (or "Web")
  • ~sitecollection – reference to the current SharePoint site collection (or "Site")
  • ~layouts – version specific reference to the web application Layouts folder (so it will automatically swap out /_layouts/14 or /_layouts/15 for you)
  • ~sitecollectionlayouts – reference to the layouts folder in the current site collection (e.g. /sites/team/_layouts/15)
  • ~sitelayouts – reference to the layouts folder in the current site (e.g. /sites/teams/subsite/_layouts/15)

 

How to make your own custom rendering

Everything starts with a List or Library, in our example, we wanted to add a place in our Intranet, where employees could share/sell stuff between them, so we called it "MarketPlace".

 

Here's the Picture Library:

 

These are the fields/columns we'll be using:

 

Last step, is to have a list view, which includes all the fields/columns we'll use in our CSR file, so we created a new view and called it "Home JSLink" (the name is irrelevant)

 

Another important detail:

This view must have the "logic" of what exactly we want to display (in terms of data). That means not only the columns, but also any filtering, sorting and number of results we want our "working dataset" to have.

For example if we want to display only 3 items and ordered alphabetically, then THAT logic should be "baked" into the view we'll use to "feed" our custom rendering.

 

We could do some filtering/sorting on the JavaScript code, but that will make it more complex to maintain. Using a simple view, we can change filters, sorting or item limits without touching the code at all!

 

Creating static HTML mockup/template

Next step is to create a static HTML mockup, prototype, template, etc., with the intended design, you could use the tool of your choice for this task.

 

Since I'm an old-fashioned guy and I like the "old" WYSIWYG designers, I tend to use Dreamweaver or Visual Studio to have a preview of the HTML layout, but I ended up changing a bit and used Visual Studio Code.

In this case we're replacing a Picture Library list view, with our custom thumbnails, but you could build a table, or a list of items, whatever works best for you:

 

This is how it looks like our template:

 

Here's the code, a pretty simple HTML list with some content inside and some CSS to make it look nicer.

If possible try to avoid any JS & CSS library or framework that could break SharePoint UI (such as Bootstrap), I used plain, vanilla, HTML, CSS and jQuery.

 

<!doctype html>

<html>

<head>

<meta charset="utf-8">

<title>MarketPlace</title>

<base href="http://spdev2013:2000/" />

<link rel="stylesheet" type="text/css" href="/_layouts/15/1033/styles/Themable/corev15.css"/>

<style type="text/css">

        .marketplace-rows {

            list-style-type:none;

            margin:0;

            padding:0;

        }

        .marketplace-rows li {

            margin:10px;

            border:1px solid transparent;

            border-bottom-color:#eee;

            float:left;

            width:260px;

            height:230px;

        }

        .marketplace-tileview-root {

            width:250px;

            height:150px;

            margin:auto;

            /*border:1px solid:#dddddd;*/

            box-shadow: 3px 3px 6px #dddddd;

        }

        .marketplace-tileview-root:hover {

            box-shadow: 0 0 6px rgba(35, 173, 278, 1);

        }

        .marketplace-tileview-content {

            width:250px;

            height:150px;

            margin:auto;

            overflow:hidden;

            text-align:center;

        }

        .marketplace-title {

            font-family:"Segoe UI", Verdana, sans-serif;

            font-size:17px;

            color:#0000ff;

        }

        .marketplace-price {

            font-family:"Segoe UI", Verdana, sans-serif;

            font-size:22px;

            color:#000000;

            text-align:right;

        }

        .marketplace-description {

            font-family:"Segoe UI", Verdana, sans-serif;

            font-size:12px;

            color:#bbbbbb;

        }

        .marketplace-offer-type {

            font-family:"Segoe UI", Verdana, sans-serif;

            font-size:15px;

            color:#000000;

            text-align:right;

        }

</style>

</head>

<body>

    <!--Begin view header-->

    <div id="divMarketPlace">

        <p>

    <span class="ms-rteStyle-HeaderOutline2">MarketPlace</span>

        </p>

        <ul class="marketplace-rows itemHover">

        <!--End view header-->

            <!--Begin thumbnail tile-->

    <li>

    <div class="marketplace-tileview-root">

    <div class="marketplace-tileview-content">

    <a href="#" title="Click to open this offer in a detailed view">

    <img src="/MarketPlace/_w/bike for sell_jpg.jpg" style="height:150px; border:0;" />

</a>

</div>

</div>

<table cellpadding="0" cellspacing="0" width="95%" style="margin:auto; margin-top:10px;">

    <tr>

    <td class="marketplace-title">Great Race Bike</td>

<td class="marketplace-price">$200</td>

</tr>

<tr>

    <td colspan="2">Have fun with this great bike.</td>

</tr>

<tr>

    <td colspan="2">San Diego, CA</td>

</tr>

<tr>

     <td>Seller: John Doe</td>

    <td class="marketplace-offer-type">SALE</td>

</tr>

</table>

            </li>

            <!--End thumbnail tile-->

        …… other rows here…… (one for each item on the list)

        <!--Begin view footer-->

        </ul>

    </div>

    <!--End view footer-->

</body>

</html>

 

Pay attention to the head section (highlighted in light blue), I'm using a local SharePoint site (in a VM), and also adding to the page the out-of-the-box SharePoint core CSS.

So we build our design and styles on top of that. This way the mockup will look the same when deployed to a real SharePoint site.

 

Please note there're three well defined sections:

  • List view header (in orange)
  • Item row (in green, we'll repeat this section one for each item on the list)
  • List view footer (in yellow)

Once we're happy with our HTML template, it's time to start building the JSLink/CSR file, slicing our HTML template.

 

Our example is around 210 lines of code and has a few sections:

  • Define CSS styles
  • Define CSR object context
    • Header (place here corresponding HTML markup from orange section)
    • Footer (place here corresponding HTML markup from yellow section)
    • List template type
    • Pre-render and post-render events
    • Item template (place here corresponding HTML markup from green section)
    • Final registration of our custom CSR within JSOM
  • Build the actual Item Template
  • Add Pre-Render and/or Post-Render code if needed
  • Common/helper functions

We'll go thru each item in detail:

 

First, define CSS Styles

Here we need to use our CSS from the HTML template and save it into a variable:

 

// Use "Multi String" javascript to embed the required css

var MultiString = function (f) {

return f.toString().split('\n').slice(1, -1).join('\n');

}

var marketplaceStyle = MultiString(function () {/**

        .marketplace-rows {

            list-style-type:none;

            margin:0;

            padding:0;

        }

        .marketplace-rows li {

            margin:10px;

            border:1px solid transparent;

            border-bottom-color:#eee;

            float:left;

            width:260px;

            height:250px;

        }

        .marketplace-tileview-root {

            width:250px;

            height:150px;

            margin:auto;

            box-shadow: 3px 3px 6px #dddddd;

        }

        .marketplace-tileview-root:hover {

            box-shadow: 0 0 6px rgba(35, 173, 278, 1);

        }

        .marketplace-tileview-content {

            width:250px;

            height:150px;

            margin:auto;

            overflow:hidden;

            text-align:center;

        }

        .marketplace-title {

            font-family:"Segoe UI", Verdana, sans-serif;

            font-size:17px;

            color:#0000ff;

        }

        .marketplace-price {

            font-family:"Segoe UI", Verdana, sans-serif;

            font-size:22px;

            color:#000000;

            text-align:right;

        }

        .marketplace-description {

            font-family:"Segoe UI", Verdana, sans-serif;

            font-size:12px;

            color:#bbbbbb;

        }

        .marketplace-offer-type {

            font-family:"Segoe UI", Verdana, sans-serif;

            font-size:15px;

            color:#000000;

            text-align:right;

        }**/

});

 

Next, define CSR object context

(function () {

// Fallback to loading jQuery from a CDN path if the local is unavailable

(window.jQuery || document.write('<script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.0.min.js"><\/script>'));

 

// Create an object to store context information about the view that we want to change its output render

var marketplaceContext = {};

marketplaceContext.ListTemplateType = 109; // Picture Library List Template

marketplaceContext.Templates = {};

 

// Be careful when adding the template header, because it will break the default list view render

marketplaceContext.Templates.Header = '<div id="divMarketPlace">'

+ ' <p>'

+ '        <span class="ms-rteStyle-HeaderOutline2">MarketPlace</span>'

+ '  </p>'

+ '    <ul class="marketplace-rows">';

 

marketplaceContext.Templates.Footer = ' </ul>'

+ ' <a href="/Lists/News/AllItems.aspx">View all company news</a>'

+ '</div>';

 

// If needed, add OnPreRender and/or OnPostRender event handlers, in this case we'll use OnPostRender to inject our CSS before our dynamically built HTML

marketplaceContext.OnPreRender = marketplaceOnPreRender;

marketplaceContext.OnPostRender = marketplaceOnPostRender;

 

// This line of code tells TemplateManager that we want to change all HTML for our custom item row render

marketplaceContext.Templates.Item = marketplaceTemplate;

 

// Register our CSR template in SharePoint

SPClientTemplates.TemplateManager.RegisterTemplateOverrides(marketplaceContext);

})();

 

Next, build the actual Item Template

// This function provides the rendering logic

function marketplaceTemplate(ctx) {

    // MarketPlace (Picture Library) list GUID.

    // This is not mandatory but useful if we have on the same page two CSR from different lists or libraries, but from the same kind.

    // For example two calendars, two announcements, etc

    var listName = "{AC80C8D8-B5C6-4E64-A1F4-C38274729CA2}";

 

    if (ctx.listName === listName) {

        // Get fields data

var id = ctx.CurrentItem.ID;

var title = ctx.CurrentItem.Title;

var imageFileName = ctx.CurrentItem["FileLeafRef"];

var description = ctx.CurrentItem["Description"];

var seller = ctx.CurrentItem.Author[0].title;

var created = new Date(ctx.CurrentItem["Created"]);

        var productCategory = ctx.CurrentItem["ProductCategory"];

var location = ctx.CurrentItem["Location"];

var price = ctx.CurrentItem["Price"];

var offerType = ctx.CurrentItem["OfferType"];

 

        // For the image name and url, we're using a custom function to build the output value

imageFileName = buildSharePointThumbnail(imageFileName);

 

        // Return whole item html

        // Stringify the HTML markup into a variable, and replace dynamic content with the above variables, containing list data

var html = '     <li>'

        + '      <div class="marketplace-tileview-root">'

        + '      <div class="marketplace-tileview-content">'

        + '      <a href="javascript:void(0);" onclick="javascript:marketplace(' + id + ');" title="Click to open this offer in a detailed view">'

        + '      <img src="' + imageFileName + '" style="height:150px; border:0;" />'

        + ' </a>'

        + ' </div>'

        + ' </div>'

        + ' <table cellpadding="0" cellspacing="0" width="95%" style="margin:auto; margin-top:10px;">'

        + '      <tr>'

        + '     <td class="marketplace-title">' + truncateString(title, 18) + '</td>'

        + ' <td class="marketplace-price">' + price + '</td>'

        + ' </tr>'

        + ' <tr>'

        + '      <td colspan="2">' + truncateString(description, 35) + '</td>'

        + ' </tr>'

        + ' <tr>'

        + '      <td colspan="2">' + location + '</td>'

        + ' </tr>'

        + ' <tr>'

        + '      <td>Seller: ' + seller + '</td>'

        + '      <td class="marketplace-offer-type">' + offerType + '</td>'

        + ' </tr>'

        + ' </table>'

        + ' </li>';

 

return html;

}

else {

        // If list GUID doesn't match, return standard/ootb item rendering

return RenderItemTemplate(ctx);

}

}

 

Next, add pre-render/post-render code if needed

In our example, we use PostRender to inject our CSS right before the dynamically built HTML (note we use jQuery selectors and DOM manipulation for this purpose).

function marketplaceOnPreRender() {

}

 

function marketplaceOnPostRender() {

$("#divMarketPlace").prepend('<style type="text/css">' + marketplaceStyle + '</style>');

}

 

Finally, our common/helper functions

Mainly for strings manipulation and modal window handling:

/*** Common Functions ***/

function removeFileExtension(fileName) {

    var temp = fileName.replace(".jpg", "");

    temp = temp.replace(".png", "");

    temp = temp.replace(".gif", "");

    temp = temp.replace(".bmp", "");

    return temp;

}

 

function buildSharePointThumbnail(imageFileName) {

var temp = "";

if ((imageFileName == "") || (imageFileName == undefined)) {

temp = "/SiteAssets/jslink/images/NoImageAvailable.png";

}

else {

    if (imageFileName.indexOf(".jpg") != -1) {

temp = "/MarketPlace/_w/" + removeFileExtension(imageFileName) + "_jpg.jpg";

}

else if (imageFileName.indexOf(".png") != -1) {

temp = "/MarketPlace/_w/" + removeFileExtension(imageFileName) + "_png.jpg";

}

else if (imageFileName.indexOf(".gif") != -1) {

temp = "/MarketPlace/_w/" + removeFileExtension(imageFileName) + "_gif.jpg";

}

else if (imageFileName.indexOf(".bmp") != -1) {

temp = "/MarketPlace/_w/" + removeFileExtension(imageFileName) + "_bmp.jpg";

}

}

return temp;

}

 

function truncateString(longText, length) {

//This regex expression use to delete html tags from the Body field

var regex = /(<([^>]+)>)/ig;

longText = longText.replace(regex, "");

var newBodyValue = longText;

if (longText && longText.length >= length) {

newBodyValue = longText.substring(0, length) + " ...";

}

return "<span title='" + longText + "'>" + newBodyValue + "</span>";

}

 

function marketplaceShowModal(id) {

var src = "/MarketPlace/Forms/DispForm.aspx?ID=" + id;

 

var options = {

url: src,

title: "MarketPlace",

allowMaximize: true,

showClose: true

};

 

SP.UI.ModalDialog.showModalDialog(options);

}

 

function marketplaceSOD(id) {

SP.SOD.executeFunc("sp.ui.dialog.js", "SP.UI.ModalDialog.showModalDialog", function () { marketplaceShowModal(id); });

}

 

function marketplace(id) {

ExecuteOrDelayUntilScriptLoaded(marketplaceSOD(id), "sp.js");

}

 

 

Next step, create a page in your SharePoint site

It can be any type of page that supports web parts.

 

Add a web part, select "App Parts", select your MarketPlace library (or whatever name you gave it), and change the view to your "Home View".

 

Next, edit web part properties and specify the JSLink property with path to your custom JS file:

 

Apply changes, save the page and refresh. This is how we turned this:

 

Into this:

 

Keep in mind that JSLink files are cached, so every time you make an update or change to the code, be sure to Ctrl + F5 to fully refresh the page.

 

We love helping with SharePoint and it's all we do so feel free to contact us!

Mention this post and we'll send you code samples for your JSLinks project!

Comments

There are no comments for this post.