Thursday, May 29, 2014

Understand & Customize Display Templates


Display templates are new in sharepoint 2013 replacing XSLT in 2010 which will be used to customize the UI/display of the webparts. Like, we had item.xsl and control.xsl back then, its same strategy in 2013 as well. Entire UI code for a webpart is split into 2 different files, Control and item template. Control template will have the markup that creates the base look/container/header section for the webpart where as item template will  have the markup defined for each result/item displayed.

For every display template(item/control) file(.html) uploaded to the master page gallery, a respective js file will be generated if no syntax errors found. Publishing features at site collection need to be activated so sharepoint could generate those files.

To start with creating a custom item template, copy any of the OOTB template and start following its pattern and conventions. I have explained few notations to make it easy for you and myself.

Ensure all the changes are written inside the first div under the body of the template. Sharepoint reads only the markup which is inside the first div(excluding div element itself) while applying the item template to each item in the webpart.
I have copied part of the code from item_picture3lines.html for better explanation. Here, the first div is  with id=item_picture3lines. Id of the div can be changed but not the div element itself.


<body>
<div id="Item_Picture3Lines" >
<!--#_
var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + "_picture3Lines_");
var linkURL = $getItemValue(ctx, "Link URL");
linkURL.overrideValueRenderer($urlHtmlEncode);
...
...
 _#-->

<div class="cbs-picture3LinesContainer" style="font-size:11px;" id="_#= containerId =#_" data-displaytemplate="_#= $htmlEncode(dataDisplayTemplateTitle) =#_">
...

</div>
</div> </body>

Scripting
Javascript/jquery can be written by wrapping it with <!--#_    _#--> without explicit script tags. So it's going to look like,

<!--#_
Var temp;
alert("calling script from display template");
 _#-->

External js file can be included by using the function,includeScript. First parameter is always this.url

$includeScript(this.url, "~sitecollection/_layouts/15/reputation.js");

Similarly external CSS can be referenced using $includeCSS(this.url, "~sitecollection/site library/custom.css");
Read here on how to run the scripts after display template or complete DOM  render($(document).ready())


Property Mappings

To add new lines  in content search webpart or do property/managed metadata mapping in display template,
look for mso:managedPropertyMapping in item template which is responsible for mapping the columns to the variables in the display template and to show in the toolpart.

<mso:ManagedPropertyMapping msdt:dt="string">'Link URL'{Link URL}:'Path','Line 1'{Line 1}:'Title','Line 2'{Line 2}:'Author','Line 3'{Line 3}:'ModifiedOWSDATE','FileExtension'</mso:ManagedPropertyMapping>

'Link URL'{Link URL}:'Path' defines single mapping for a managed metadata.
'Link URL'{Link URL}:'Path'  - first token represents the label of the property displayed in the toolpart under property mappings

'Link URL'{Link URL}:'Path' - second token represents the display name of the variable reference to the actual managed metadata property. This variable will be used in the display template to access the value.

'Link URL'{Link URL}:'Path'  - last token is the actual name of the managed metadata that is used in display template. This is not the display name of the column but the name of the managed metadata property mapped to the crawled property in search schema.

In case of mapping multiple metadata properties to a single display template property variable, follow the below syntax
'Link URL'{Link URL}:'Path;Url'

When no mapping required but need to access the value of managed metadata inside display template just add it in single quotes separated by comma like FileExtension is added.

Context

Sharepoint by default loads the context(ctx) as part of display template that can be used to access many of the contextual properties when customizing display templates.
Access the value of a mapped property by using getitemValue.

Var value = $getItemValue(ctx, "Line 2");

$getItemValue takes the display name of the property(second token in parenthesis) as the second parameter. This way code is generic and it still works even if the property mapping is changed in webpart configuration. Also, if there are managed metadata properties that need to be used(like listItemID,ItemID) that don’t need to be listed in the property mappings, the value of it can be fetched by directly passing the manage metadata property name as per search schema.
The above line of code fetches Author value as {Line 2} is mapped to Author in the display template. Below code also fetches the same value,

Var authorVal  = $getItemValue(ctx, "Author");

Few contextual properties which can be fetched from context,

ctx.CurrentItem.ListItemID
ctx.CurrentItem.SPSiteUrl

To retrieve the current item index  or current row number use the property CurrentItemIdx from ctx  object.

Var currentItemIndex  = ctx.CurrentItemIdx;

Get the row count or total number of rows with,

Ctx.CurrentGroup.RowCount

Row count can be used in the logic when to apply alternate css/styles to the rows, where as row id to display any header section(table header)  for the entire webpart. Header section can be added to control display template but if you don’t want to change 2 different files, just check for currentitemidx=1 and add header section markup in the item template itself.  Note that these properties wont work in control display template.

To find if the results are empty inside control display template,  I checked for DOM length being empty for div.ms-srch-result-noResults.  Thought of using if (ctx.ClientControl.get_shouldShowNoResultMessage()) but did not work as expected. Yet to find a better approach.

Get the unique id by using ctx.ClientControl.get_nextUniqueId();

Access values in markup

Display value of the managed metadata property or a variable as part of the markup. Here authorVal is the variable in the above script having the value of the managed metadata property, Author.

<span>_#= authorVal =#_</span>

Tuesday, May 27, 2014

Execute script after MDS load

Share/Save/Bookmark

$(document).ready() always works when its a complete page load/refresh but not when there are controls enabled with ajax doing async callback or Minimial download strategy(sharepoint 2013) is enabled for the site that reloads only the changes in the page.Know more about Minimal Download Strategy by reading this.

Also, Its not always necessary that the page is loaded following MDS but sometime may reload. So both cases need to be handled when writing scripts(jquery/javascript)
Below is the code that i could make use of that could fulfill this scenario.

$(function () {
      ExecuteOrDelayUntilScriptLoaded(function () {
      if (typeof asyncDeltaManager != "undefined")
            asyncDeltaManager.add_endRequest(doSomething); //after MDS loads the changes
      else doSomething();
      }, "start.js");
});

function doSomething()
{
   //write something
}

 Subscribe

Friday, May 23, 2014

Autocomplete using SPServices with JQuery

Share/Save/Bookmark

Call it auto suggestion or type ahead or auto complete, its pretty similar that im trying to achieve here in SharePoint using SPServices API with JQuery.
Get your copy of SPServices js from http://spservices.codeplex.com/ and have it in a library or layouts folder so it can be referenced in the script that we gonna write.

Here, im implementing a sharepoint site page that has a custom filter option that will allow user to filter the data in mulitple listviewwebpart added to the same page.Sometime names may sound same but spell different, so i want to give auto suggestions when user starts typing the text in filter textbox.

$(document).ready(function(){
 $().SPServices.SPAutocomplete({
     sourceList:"Employee list",
     sourceColumn: "FullName", // SPField name from which data will be pulled using lists webservice
     columnName:"FilterByName", // either sp field name if using on list form or input field control title attribute
     ignoreCase:true,
     numChars:3, //autocomplete shows when there is a minimum of 3 char in the textbox of the field/control
     slideDownSpeed: 1000,
  });
});

Once value is set in filtertexbox  and submitted i have an additional script that appends the querystring FilterField1=fileldname&FilterValue1=value that will filters the data in multiple list webparts.

SPServices made developers job easy when it comes to writing client side code. Ofcourse, you can achieve the same thing by writing your own code from scratch with a jquery ajax request to sharepoint lists webservice and bind results with textbox.
Im hoping SPServices will introduce a way to find the input control not just by 'Title' but also by name/id because some sharepoint input controls(may not be list forms) will not have title attribute set.


Subscribe

start.aspx# Minimal Download Strategy

Share/Save/Bookmark

Why is that junk(start.aspx#/../) getting added in sharepoint 2013 url?
Why do the urls in sharepoint 2013 do not look like they were anymore?
Untill 2010 Sharepoint used to reload the entire page for every request sent from browser. In 2013, a site scope featured, Minimal Download Strategy(MDS) is introduced that will minimize the content to be downloaded from server when user navigates from page to page.

When request is sent from browser, the delta is calculated which is equal to the difference between the current page content and the response for the new request. This delta will be sent to MDS engine and start.aspx is responsible for loading the changed content in the page.Anything after start.aspx# represents the relative url of the target page. Page is completely reloaded only when MDS thinks its not just part of the page that changed but all of it and for other reasons listed here. This feature is optional and considered to improve any performance on the page. Be aware of this feature activation status when writing any client scripts playing with url or trying to do something on document ready, because page may not be reloading so do your scripts.


Subscribe

Thursday, May 22, 2014

Email with attachment in sharepoint

Share/Save/Bookmark

Today i had to implement a feature, email(using lotus notes) a file as an attachment in SharePoint 2013. I would concentrate here on the approach taken and leave 'how to create ribbon button' for you to google.

If we recall,SharePoint 2010 has the ribbon option, Email a link that allows user to send an email with link to the file/item of document library which even do not exist in SharePoint 2013 for document libraries. SharePoint is doing this by using mailto protocol which i considered as the option to implement my requirement leaving behind the other option that deals with building smtp messages.

msdn documentation says that mailto protocol do not support attach header anymore and so do the other blog posts. But, in my case with lotus notes i'm able to use attach option with mailto protocol to invoke lotus notes and create a new email with file attached that's ready to send. Not sure if this works with latest versions of IE and outlook. Note that mailto invokes the default email client set on the user machine.

Syntax:
mailto:?subject=shared a link&body=message&attach=\\domain.com\sites\dept\filename.docx

Path that is passed to attach attribute recognized by WebDAV to connect to sharepoint site and loads the file.Its pretty similar to how explorer view works in sharepoint. Files in sharepoint can be accessed outside either by using WebDAV or FPRPC.
Ensure to encode the special characters in the above line.

It worked so well and a quick implementation but lately i realized it not working with https sites which has become a roadblock. After so much of play with webdav url pattern i found the solution and have to modify the protocol to,

mailto:?subject=shared a link&body=message&attach=\\domain.com@SSL\sites\dept\filename.docx

That way im able to convey to webdav that the location of the file is on HTTPS site and the authentication is successful. Though it worked on all windows machines its a different story on how to make it work on MAC /apple machines(with finder).


Subscribe

Run custom script after display templates render

Share/Save/Bookmark
I have been trying to run some custom javascript after $(document).ready in sharepoint control display template and stuck very badly.No matter where i place the js code in the display template(html) file, DOM is not ready by the time document.ready executes. So elements references in the script always return null. It seems that DOM rendering and the way MS handled events is different in the case of the display templates.

Exploring other templates, figured out that below code works when added to control display template. Bind the function that need to be called with OnPostRender event on the ClientContext object, ctx(instantiated by sharepoint)

ctx.OnPostRender = [];

ctx.OnPostRender.push(function () {
   alert("Document is ready");
  // logic goes here
});

In the above code post render callback is used to call custom script that will run after every script is called and complete DOM is rendered in display template.
OnPostRender = $(document).ready when working with sharepoint display templates. No more scratching.
Custom logic can be moved to an external file and refered in the display template as below or call it by using SP.SOD functions,

$includeScript(this.url, https://sharepointcustomization.blogspot.com/js/jquery.min.js);

Subscribe

Wednesday, May 21, 2014

Sharepoint content cannot be displayed in an IFrame

Share/Save/Bookmark

"This content cannot be displayed in IFrame.To help protect the security of information you enter into this website the publisher of this content does not allow it to be displayed in a frame"

You may be experiencing the above error if you are trying to open a sharepoint page/webpart page in iframe(say ModalDialog) either it could be cross domain or in same domain.

Sharepoint 2013 has added settings to control the source of iframe at site collection level under HTML Field security. This will only allow the current domain/page to accept the content from the specific domain that may be added in the iframe settings. But if you read the top error message, the source domain/page thats rendering in iframe itself is responsible for the behavior not the destination page/domain. So this setting would not resolve the issue. To get rid of the error add the below tag to masterpage/page layout/application page/visual webpart as required..

<WebPartPages:AllowFraming ID="AllowFraming1"runat="server"/>


 Subscribe