This library can be used even on OnPrem environments. It includes most of the common operations we do in JS for SharePoint.
Download JS file from GitHub
This library can be used even on OnPrem environments. It includes most of the common operations we do in JS for SharePoint.
Download JS file from GitHub
This post is about “ScrollToTopOnRedraw” property of search results webpart. The page scrolls to top after a search operation (search, refinment, pagination) if set to true. It is a boolean property which is false by default and All we need to do is set this boolean value to true for our search results webpart/ search context.
We can set this property in Two ways:
1) Export your current search results webpart to get .webpart file
2) Open it in a text editor (VS)
3). Find the property “ScrollToTopOnRedraw” and change its value from False to True and Save the file.
4).Import the webpart and add it in your page.
Thats All. The page scrolls to top when you click on pagination links / apply refiners / apply search.
This is the simplest way to achieve the scroll if you are using custom control display template for your search results.
The OOTB “Control_SearchResults.html” control template looks like
<div id="Control_SearchResults"> <!--#_ if (Srch.U.shouldAnimate(ctx.DataProvider)){ Srch.U.hideElement(ctx.ClientControl.get_element()); ctx.OnPostRender = function(){ Srch.U.animateResults(ctx.ClientControl, ctx.DataProvider.get_userAction()); }; } _#-->
Add a line of code at the top of if condition to change the property value
<div id="Control_SearchResults"> <!--#_ //scroll to top on redraw(pagination, refiner, search) ctx.ClientControl.set_scrollToTopOnRedraw(true); if (Srch.U.shouldAnimate(ctx.DataProvider)){ Srch.U.hideElement(ctx.ClientControl.get_element()); ctx.OnPostRender = function(){ Srch.U.animateResults(ctx.ClientControl, ctx.DataProvider.get_userAction()); }; } _#-->
This post is just another example about JSLink you can find on web. This post is about show follow/Unfollow Document link in List-view. The solution is simple. Add a text column on the List (Say FollowLink) and hide this column in forms. Include the column in Listview. Link the following JSLink code in Listview webpart.
var SPOFieldCustomizations = window.SPOFieldCustomizations || {}; SPOFieldCustomizations.CustomizeFollowLinkFieldRendering = function () { var fieldJsLinkOverride = {}; fieldJsLinkOverride.Templates = {}; fieldJsLinkOverride.OnPostRender = postRenderHandler; fieldJsLinkOverride.Templates.Fields = { // Make sure the Priority field view gets hooked up to the GetPriorityFieldIcon method defined below // we have to update the column name here 'FollowLink': { 'View': SPOFieldCustomizations.GetFollowLinkFieldMarkup } }; // Register the rendering template SPClientTemplates.TemplateManager.RegisterTemplateOverrides(fieldJsLinkOverride); }; SPOFieldCustomizations.GetFollowLinkFieldMarkup = function (ctx) { var followLinkMarkup = ""; if (ctx.CurrentItem.FSObjType === "0") { followLinkMarkup = '<a class="ms-profile-followLink ms-heroCommandLink" href="javascript:;">' + '<span class="ms-profile-followHeroImageParent">' + '<img data-toggle="tooltip" data-placement="right" title="" id="Img' + ctx.CurrentItem.ID + '" src="/_layouts/15/images/gears_anv4.gif?rev=35" class="ms-profile-followHeroImage" ></img>' + '</span>' + '</a>'; SPOFieldCustomizations.isFollowed('/' + ctx.CurrentItem.FileRef, 'Img' + ctx.CurrentItem.ID); } return followLinkMarkup; }; SPOFieldCustomizations.isFollowed = function (documentUrl, imgid) { window.Utilities.Social.REST.LoadIsFollowed(documentUrl, _spPageContextInfo.webAbsoluteUrl, 1, function (responseData) { jsonObject = JSON.parse(responseData.body); if (jsonObject.d.IsFollowed === true) { $('#' + imgid).attr("title", "Click to Unfollow").attr('data-followed', '1') .attr('src', '/_layouts/15/images/socialcommon.png?rev=33') .addClass("unFollow") } else { $('#' + imgid).attr("title", "Click to Follow").attr('data-followed', '0') .attr('src', '/_layouts/15/images/socialcommon.png?rev=33') .removeClass("unFollow"); } $('#' + imgid).click(function () { var FollowStatus = $(this).attr('data-followed'); var url = ''; if (FollowStatus === '1') url = _spPageContextInfo.webAbsoluteUrl + '/_api/social.following/stopfollowing'; else if (FollowStatus === '0') url = _spPageContextInfo.webAbsoluteUrl + '/_api/social.following/follow'; if (url !== '') { SPOFieldCustomizations.toggleFollow(url, documentUrl, imgid, FollowStatus); } }); }, function (s, a, errMsg) { $('#' + imgid).text(errMsg); }); }; SPOFieldCustomizations.toggleFollow = function (url, documentUrl, imgid, FollowStatus) { window.Utilities.Social.REST.ToggleFollow(url, documentUrl, _spPageContextInfo.webAbsoluteUrl, 1, function (responseData) { var jsonObject = JSON.parse(responseData.body); var isfollowed = jsonObject.d.Follow; var stoppedFollowing = jsonObject.d.StopFollowing; if (isfollowed !== undefined) { $('#' + imgid).attr('title', 'Click to Unfollow').attr('data-followed', '1') .addClass("unFollow"); } else { $('#' + imgid).attr('title', 'Click to Follow').attr('data-followed', '0') .removeClass("unFollow"); } }, function (s, a, errMsg) { $('#' + imgid).text(errMsg); }); } function postRenderHandler(ctx) { // code to execute after view render } // Call the function. SPOFieldCustomizations.CustomizeFollowLinkFieldRendering();
There are few utility function calls inside this JSLink code to follow/unfollow documents. The code looks like
function LoadAndExecuteSodFunction(scriptKey, fn) { if (!ExecuteOrDelayUntilScriptLoaded(fn, scriptKey)) { LoadSodByKey(NormalizeSodKey(scriptKey)); } } RegisterSod('/_layouts/15/sp.requestexecutor.js'); window.Utilities = window.Utilities || {}; window.Utilities.Social = window.Utilities.Social || {}; window.Utilities.Social.REST = window.Utilities.Social.REST || function () { var LoadIsFollowed = function (documentOrSiteUrl, siteurl, actortype, successcallback, failcallback) { var requestinfo = { url: siteurl + '/_api/social.following/isfollowed', method: "POST", body: JSON.stringify({ "actor": { "__metadata": { "type": "SP.Social.SocialActorInfo" }, "ActorType": actortype, "ContentUri": documentOrSiteUrl, "Id": null } }), headers: { "accept": "application/json;odata=verbose", "content-type": "application/json;odata=verbose" }, success: function (responseData) { successcallback(responseData); }, error: function (s, a, errMsg) { failcallback(s, a, errMsg); } }; LoadAndExecuteSodFunction('sp.requestexecutor.js', function () { var executor = new SP.RequestExecutor(siteurl); executor.executeAsync(requestinfo); }); }; var ToggleFollow = function (url, documentOrSiteUrl, siteurl, actortype, successcallback, failcallback) { var requestinfo = { url: url, method: "POST", body: JSON.stringify({ "actor": { "__metadata": { "type": "SP.Social.SocialActorInfo" }, "ActorType": actortype, "ContentUri": documentOrSiteUrl, "Id": null } }), headers: { "accept": "application/json;odata=verbose", "content-type": "application/json;odata=verbose" }, success: function (responseData) { successcallback(responseData); }, error: function (s, a, errMsg) { failcallback(s, a, errMsg); } }; LoadAndExecuteSodFunction('sp.requestexecutor.js', function () { var executor = new SP.RequestExecutor(siteurl); executor.executeAsync(requestinfo); }); }; return { LoadIsFollowed: LoadIsFollowed, ToggleFollow: ToggleFollow}; }();
The first thing I really like about SharePoint REST APIs is, we do not need any reference to client assemblies or JS libraries. The basic operations in REST API includes CRUD operations on lists. You can find the list of these end points on MSDN. And we can find so many examples on MSDN and other websites using REST APIs with jQuery $.ajax.
Here is why I’m not a big fan of $.ajax with SharePoint APIs, for two reasons.
I’ve used $.ajax for adding a list item and I thought it will be working fine.
var AddListItem = function(product) { return $.Deferred(function (def) { var data = { __metadata: { type: 'SP.Data.ProductsListItem' }, Title: product.ProductName, ProductNumber: product.ProductNumber, Price: product.Price }; $.ajax({ url: webUrl + "/_api/web/lists/getbytitle('Products')/items", type: 'POST', contentType: 'application/json;odata=verbose', data: JSON.stringify(data), headers: { 'Accept': 'application/json;odata=verbose', 'X-RequestDigest': $('#__REQUESTDIGEST').val() }, success: function (data) { def.resolve(data) }, error: function (s,a,errMsg) { def.reject(errMsg) } }); }); };
But I see a problem with POST requests. we need to include X-RequestDigest parameter in request and the Request digest value expires in 18 seconds(AFAIK). The code worked fine in my office365 environment. But not worked when I make a POST call after 18 seconds. The RequestDigest timeout was very less in one of my on-premise environments so the code was never able to make POST operations. returned error message like 403/401. So I removed $.ajax and used SharePoint cross domain JS library sp.requestexecutor.js to make REST calls.
var AddListItem = function (product) { var executor = new SP.RequestExecutor(webUrl); return $.Deferred(function (def) { var url = webUrl + ";/_api/web/lists/getbytitle('Products')/items"; var data = { __metadata: { type: 'SP.Data.ProductsListItem' }, Title: product.ProductName, ProductNumber: product.ProductNumber, Price: product.Price }; executor.executeAsync({ url: url, method: 'POST', body: JSON.stringify(data), headers: { 'Accept': 'application/json; odata=verbose', 'content-type': 'application/json;odata=verbose'; }, success: function(data){def.resolve(data)}, error: function(s,a,errMsg){def.reject(errMsg)} }); }); };
Now, this code looks same like the code we use to make REST calls in Cloud hosted apps and it is same except the URL and url in SP.RequestExecutor() Constructor. The URL in cloud hosted apps looks like
appweburl +"/_api/SP.AppContextSite(@target)/web/lists/getbytitle('Products')/items?@target=" +hostweburl
We are not using the key Object of cross domain API “SP.AppContextSite” here and the URL is same as we use with $.ajax. The second Difference is we are creating ‘SP.RequestExecutor()’ to current site URL(not any appWebUrl). The benefit here we get is we need not to pass X-RequestDigest parameter. This cross domain library will take care of getting the Latest Request Digest from server(https://site/_api/contextInfo) and append it to Request. This code worked perfectly in all of my environments (Office 365 and OnPremise). You need to load /_layouts/15/sp.requestexecutor.js‘ to use this library.
In one of my projects we implemented a site collection which have list of community sub sites and you can follow / unfollow those communities from a WebPart at site collection level. And some of those communities are Private(with unique permissions). Assume you are using $.ajax to get the follow status / to follow / to unfollow. The code to get the status of Follow looks like.
var followingManagerEndpoint = webUrl + &amp;quot;/_api/social.following&amp;quot;;// you can use rootSite Url here function isFollowed() { $.ajax( { url: followingManagerEndpoint + &amp;quot;/isfollowed&amp;quot;, type: &amp;quot;POST&amp;quot;, data: JSON.stringify( { &amp;quot;actor&amp;quot;: { &amp;quot;__metadata&amp;quot;: { &amp;quot;type&amp;quot;:&amp;quot;SP.Social.SocialActorInfo&amp;quot; }, &amp;quot;ActorType&amp;quot;:SP.Social.SocialActorType.site, &amp;quot;ContentUri&amp;quot;:webUrl, &amp;quot;Id&amp;quot;:null } }), headers: { &amp;quot;accept&amp;quot;:&amp;quot;application/json;odata=verbose&amp;quot;, &amp;quot;content-type&amp;quot;:&amp;quot;application/json;odata=verbose&amp;quot;, &amp;quot;X-RequestDigest&amp;quot;:$(&amp;quot;#__REQUESTDIGEST&amp;quot;).val() }, success: function (responseData) { stringData = JSON.stringify(responseData); jsonObject = JSON.parse(stringData); if (jsonObject.d.IsFollowed === true ) { alert('The user is currently following the site.'); //stopFollowSite(); } else { alert('The user is currently NOT following the site.'); //followSite(); } }, error: requestFailed }); }
Assume that you are making this call to get the follow status of a private to which you do not have access. You cannot make a call with url as “webUrl + “/_api/social.following” because you do not have access to webUrl. So you can try rootSiteUrl in followingManagerEndPoint and pass the subsiteurl in contentUri parameter. It still thrown me an error(403 or 401, do not remember). The solution is to get rid of $.ajax and use SP.RequestExecutor(rootSiteUrl).
var LoadIsFollowed = function (documentOrSiteUrl, rootSiteUrl, actortype, successcallback, failcallback) { var requestinfo = { url: rootSiteUrl+ '/_api/social.following/isfollowed', method: &amp;quot;POST&amp;quot;, body: JSON.stringify({ &amp;quot;actor&amp;quot;: { &amp;quot;__metadata&amp;quot;: { &amp;quot;type&amp;quot;: &amp;quot;SP.Social.SocialActorInfo&amp;quot; }, &amp;quot;ActorType&amp;quot;: actortype, &amp;quot;ContentUri&amp;quot;: documentOrSiteUrl, &amp;quot;Id&amp;quot;: null } }), headers: { &amp;quot;accept&amp;quot;: &amp;quot;application/json;odata=verbose&amp;quot;, &amp;quot;content-type&amp;quot;: &amp;quot;application/json;odata=verbose&amp;quot; }, success: function (responseData) { successcallback(responseData); }, error: function (s, a, errMsg) { failcallback(s, a, errMsg); } }; var executor = new SP.RequestExecutor(rootSiteUrl); executor.executeAsync(requestinfo); };
Similarly we can implement code to follow/unfollow the subsite even you do not have access to it.