Add To Favorites in SharePoint

In a recent project, a co-worker described a scenario where users navigate to a site and review a handful of up to 30 or 40 reports/documents stored in the site. Each user visits the site on a periodic basis to review these various reports. My co-worker proposed allowing users to place links to the reports they need to review each period in a links library and then filter the default view to show links created by the current user. The client wanted to make it as easy as possible to add the report by including it as an option on the edit control block. In most cases, this would call for a farm solution that deploys a new ECB custom action. However, farm solutions are off limits — and this is a SharePoint 2007 environment so SPD custom actions are not available either.

Continue reading “Add To Favorites in SharePoint”

Advertisements

Get User Roles using jQuery and SPServices

If you ever have a need to get the roles a user is assigned using jQuery – here’s how. You will need to get jQuery (www.jquery.com) and SPServices (spservices.codeplex.com). Then you can perform the following:
Continue reading “Get User Roles using jQuery and SPServices”

InfoPath over jQuery?

A recent project had my team asking some really difficult questions on what technology should be applied to a business problem. We had been tasked to create a specialized survey in SharePoint that would need to change based on the items selected for each question. The question would present five or six possible answers, but all other answers might need to be cleared if a specific answer was selected.

For example, the question might read “Who needs to sign-off on this project?” and include “Information Technology, Public Relations, Marketing, Executive Leadership and No Sign-off Required” as possible answers. The person completing the survey could select any combination of the first four options. However; if the person completing the survey selected No Sign-off Required, all of the other answers become irrelevant so they should be cleared.

Continue reading “InfoPath over jQuery?”

MS Word Special Characters “MAY” break SharePoint Web Services

Today I was working with a client to resolve an issue with a jQuery solution we provided several months ago. The solution is a policy and procedures site that makes use of several filtered views with jQuery providing some “enhancements”. The “enhancements” were being handled dynamically with jQuery and the SPServices library by Marc Anderson.

Continue reading “MS Word Special Characters “MAY” break SharePoint Web Services”

Quick Way to Load SPServices

In a lot of the projects I work on daily, I find myself using the SPServices library (by Marc Anderson) for a lot of different types of interaction. Sometimes, I wrap up all of the functionality within a content editor web part that can be exported and potentially placed in another location of the site. jQuery is pretty much always loaded on these sites, but the SPServices library may not be loaded. Here’s a quick way to make sure that it’s available for web parts that will make use of the library.
Continue reading “Quick Way to Load SPServices”

Improve jQuery Performance by using Asynchronous AJAX

In a recent project, I needed to make some calls using AJAX to enable and disable buttons in a data view. The data view is presenting a list of individuals filtered by the first letter of the last name so that tabs can be provided for each group of individuals. To improve the user experience, the team decided to fade tabs that currently had no items listed. This meant that I need to make 26 web service calls to SharePoint for each letter of the alphabet.

Of course, the best way to interact with web services using jQuery is to download Marc Anderson’s (@sympmarc) SPServices jQuery Library from CodePlex. With this in hand, I started building my code to execute the queries.

First, my data view creates an unordered list with each letter of the alphabet. This ties into a dataview looking for the querystring D to filter values.

<div id="FilterSelector">
	<div id="Loader">
		Loading data...
	</div>
	<span id="FilterLabel">Last Name Starts With: </span>
	<ul id="FilterSelectorTabs">
		<li><a href="?D=A" id="SELECT_A">A</a></li>
		<li><a href="?D=B" id="SELECT_B">B</a></li>
		<li><a href="?D=C" id="SELECT_C">C</a></li>
		<li><a href="?D=D" id="SELECT_D">D</a></li>
		<li><a href="?D=E" id="SELECT_E">E</a></li>
		<li><a href="?D=F" id="SELECT_F">F</a></li>
		<li><a href="?D=G" id="SELECT_G">G</a></li>
		<li><a href="?D=H" id="SELECT_H">H</a></li>
		<li><a href="?D=I" id="SELECT_I">I</a></li>
		<li><a href="?D=J" id="SELECT_J">J</a></li>
		<li><a href="?D=K" id="SELECT_K">K</a></li>
		<li><a href="?D=L" id="SELECT_L">L</a></li>
		<li><a href="?D=M" id="SELECT_M">M</a></li>
		<li><a href="?D=N" id="SELECT_N">N</a></li>
		<li><a href="?D=O" id="SELECT_O">O</a></li>
		<li><a href="?D=P" id="SELECT_P">P</a></li>
		<li><a href="?D=Q" id="SELECT_Q">Q</a></li>
		<li><a href="?D=R" id="SELECT_R">R</a></li>
		<li><a href="?D=S" id="SELECT_S">S</a></li>
		<li><a href="?D=T" id="SELECT_T">T</a></li>
		<li><a href="?D=U" id="SELECT_U">U</a></li>
		<li><a href="?D=V" id="SELECT_V">V</a></li>
		<li><a href="?D=W" id="SELECT_W">W</a></li>
		<li><a href="?D=X" id="SELECT_X">X</a></li>
		<li><a href="?D=Y" id="SELECT_Y">Y</a></li>
		<li><a href="?D=Z" id="SELECT_Z">Z</a></li>
	</ul>
</div>

My first attempt was done synchronously calling the lists web service to get a count of items. To do this, I simply iterated through each item in my unordered list and called the web service with an appropriate CAML query:

function getAdditionalData(tabs)
{
		$(tabs).find("li").each(function () {
			var item = $(this).find("a");
			var itemValue = item.text();
			var itemCount = getItemCounts(itemValue);
			if(itemCount == '0')
			{
				item.attr("isempty","yes");
				item.attr("title","There are currently no tributes found.");
			}
			else
			{
				item.attr("isempty","no");
				item.attr("title",itemCount + " tribute(s) found.");
			}
		});
}

function getItemCounts(displayValue)
{
	var countOfItems = 0;
	$().SPServices({
		operation: "GetListItems",
		async: false,
		listName: "Contacts",
		CAMLQuery: "<Query><Where><And><BeginsWith><FieldRef Name=\"lastname\" /><Value Type=\"Text\">" + displayValue + "</Value></BeginsWith><Eq><FieldRef Name=\"ContactStatus\" /><Value Type=\"Choice\">Approved</Value></Eq></And></Where></Query>",
		completefunc: function (xData, Status) {
			if(Status == "success") {
				countOfItems = $(xData.responseXML).find("[nodeName=rs:data]").attr("ItemCount");
			}
		}
	});
	return countOfItems;
}

This worked perfectly, but ended up locking the browser until all 26 queries were executed. As the list continued to grow, this delay became more and more noticable (and we’re only at 280 items!!). Each of the 26 calls required around 200 ms on average. Doing some quick math, you can see that the load time for the page was roughly 5,200 ms or about 5 seconds. This is unacceptable and could lead to poor reception from our user community. It was necessary to review the code to see if we could get some performance gains.

The team decided the best way to get some performance gains would be to make the web call asynchronously. When the AJAX call completes, allow the user interface to be updated at that time. Here’s the code we used:

	function getAdditionalData(tabs)
	{
		tabs.find("li").each(function () {
			var item = $(this).find("a");
			var itemValue = item.text();
			var Query = "<Query><Where><And><BeginsWith><FieldRef Name=\"lastname\" /><Value Type=\"Text\">" + itemValue + "</Value></BeginsWith><Eq><FieldRef Name=\"ContactStatus\" /><Value Type=\"Choice\">Approved</Value></Eq></And></Where></Query>";
			$().SPServices({
				operation: "GetListItems",
				async: true,
				listName: "Contacts",
				CAMLQuery: Query,
				completefunc: function (xData, Status) {
					if(Status == "success")
					{
						var itemCount = $(xData.responseXML).find("[nodeName=rs:data]").attr("ItemCount");
						if(itemCount == '0')
						{
							item.fadeTo(100,.3);
							item.attr("title","There are currently no tributes found.");
						}
						else
						{
							item.attr("title", itemCount + " tribute(s) found");
						}
					}
					if(itemValue == "Z")
					{
						$("#Loader").fadeOut(400);
					}
				}
			});
		});
	}

By making the web service calls asynchronously, the loading time of the page has reduced to around 200 ms. Also, the page is fully responsive while the web service calls are occuring, so if the user chooses to navigate to another tab they do not have to wait until all of the data has been processed by the server.

— UPDATED —
To help clarify the above statement, the page is completely ready for browsing with all web service calls completed in about 200 ms.