Dev Notes

Notes on Development with Microsoft Technologies


11 Comments

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.

Advertisements


3 Comments

Update CAML Query for SharePoint Data View Web Part

In a recent project, it was necessary to change the query being executed for a highly customized data view web part. One of the challenges you face when creating heavily customized data view web parts is the difficulty of changing the query through SharePoint Designer’s UI. Here’s a simple technique that should help.

When a DVWP is placed into the page, an SPDataSource is also created. This control has an attribute named SelectCommand that contains the CAML Query executed by the DVWP. You can go to the code and change it to whatever you need it to be.

	<DataSources>
		<SharePoint:SPDataSource runat="server" DataSourceMode="List" UseInternalName="true" selectcommand="&lt;Query&gt;&lt;Where&gt;&lt;Eq&gt;&lt;FieldRef Name=&quot;servicestatus&quot; /&gt;&lt;Value Type=&quot;Choice&quot;&gt;{Service}&lt;/Value&gt;&lt;/Eq&gt;&lt;/Where&gt;&lt;OrderBy&gt;&lt;FieldRef Name=&quot;fullname&quot; Ascending=&quot;True&quot; /&gt;&lt;/OrderBy&gt;&lt;/Query&gt;" id="Veterans1"><SelectParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BEAC53BD-E139-46C9-8517-E21891D9AA3B"/><WebPartPages:DataFormParameter Name="Directory" ParameterKey="Directory" PropertyName="ParameterValues" DefaultValue="B"/></SelectParameters><DeleteParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BEAC53BD-E139-46C9-8517-E21891D9AA3B"/></DeleteParameters><UpdateParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BEAC53BD-E139-46C9-8517-E21891D9AA3B"/></UpdateParameters><InsertParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BEAC53BD-E139-46C9-8517-E21891D9AA3B"/></InsertParameters></SharePoint:SPDataSource>
	</DataSources>

Here’s my original query:
&lt;Query&gt;&lt;Where&gt;&lt;Eq&gt;&lt;FieldRef Name=&quot;servicestatus&quot; /&gt;&lt;Value Type=&quot;Choice&quot;&gt;{Service}&lt;/Value&gt;&lt;/Eq&gt;&lt;/Where&gt;&lt;OrderBy&gt;&lt;FieldRef Name=&quot;fullname&quot; Ascending=&quot;True&quot; /&gt;&lt;/OrderBy&gt;&lt;/Query&gt;

Notice that the query is HTML Encoded. Here’s how the query appears when decoded:

<Query><Where><Eq><FieldRef Name="servicestatus" /><Value Type="Choice">{Service}</Value></Eq></Where><OrderBy><FieldRef Name="fullname" Ascending="True" /></OrderBy></Query>

One of my dynamic properties appears in this query enclosed in brackets (i.e. {Service}). I can now simply change the query and ensure that it is HTML encoded as a replacement to the original SelectCommand:

<Query><Where><And><Eq><FieldRef Name="servicestatus" /><Value Type="Choice">{Service}</Value></Eq><Eq><FieldRef Name="TributeStatus" /><Value Type="Choice">Approved</Value></Eq></And></Where><OrderBy><FieldRef Name="fullname" Ascending="True" /></OrderBy></Query>

This makes the SPDataSource appear as follows:

	<DataSources>
		<SharePoint:SPDataSource runat="server" DataSourceMode="List" UseInternalName="true" selectcommand="&lt;View&gt;&lt;Query&gt;&lt;Where&gt;&lt;And&gt;&lt;Eq&gt;&lt;FieldRef Name=&quot;servicestatus&quot; /&gt;&lt;Value Type=&quot;Choice&quot;&gt;{Service}&lt;/Value&gt;&lt;/Eq&gt;&lt;Eq&gt;&lt;FieldRef Name=&quot;TributeStatus&quot; /&gt;&lt;Value Type=&quot;Choice&quot;&gt;Approved&lt;/Value&gt;&lt;/Eq&gt;&lt;/And&gt;&lt;/Where&gt;&lt;OrderBy&gt;&lt;FieldRef Name=&quot;fullname&quot; Ascending=&quot;True&quot; /&gt;&lt;/OrderBy&gt;&lt;/Query&gt;&lt;/View&gt;" id="Veterans1"><SelectParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BEAC53BD-E139-46C9-8517-E21891D9AA3B"/><WebPartPages:DataFormParameter Name="Directory" ParameterKey="Directory" PropertyName="ParameterValues" DefaultValue="B"/></SelectParameters><DeleteParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BEAC53BD-E139-46C9-8517-E21891D9AA3B"/></DeleteParameters><UpdateParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BEAC53BD-E139-46C9-8517-E21891D9AA3B"/></UpdateParameters><InsertParameters><WebPartPages:DataFormParameter Name="ListID" ParameterKey="ListID" PropertyName="ParameterValues" DefaultValue="BEAC53BD-E139-46C9-8517-E21891D9AA3B"/></InsertParameters></SharePoint:SPDataSource>
	</DataSources>


1 Comment

Silverlight and Sharepoint 2010 a step forward: how to build a small Silverlight 4 utility to upload files in a List or Library of Sharepoint

I’ve been reading a lot of resources related to Silverlight, especially as it relates to SharePoint 2010 development. One of the things that impresses me most about the technology is the ability to deliver very rich user interfaces. Not only that, it allows some advanced interactions that are simply not possible without some heavy coding, solutions like this:

Silverlight and Sharepoint 2010 a step forward: how to build a small Silverlight 4 utility to upload files in a List or Library of Sharepoint.

This solution shows how to upload a file to a document library simply by selecting the files from your file system and then dragging them over the Silverlight application where they are processed and placed into SharePoint. I’m imagining all types of scenarios where this would be useful — especially for business users that deal with a large volume of files every day.


1 Comment

Referencing Javascript Files with SharePoint 2010 Custom Actions using SciptSrc – Jan Tielens’ Bloggings

I began searching tonight to see what was available for the silverlight javascript side files in SharePoint 2010 and stumbled upon this little GEM:

Referencing Javascript Files with SharePoint 2010 Custom Actions using SciptSrc – Jan Tielens’ Bloggings.

This is an incredibly powerful capability — including JavaScript libraries like jQuery can be turned into features that are activated on the individual sites where they are needed!


2 Comments

Using hMailServer with SharePoint

Building VM’s for development is almost a necessary skill. One of the most difficult things to do is get all of the necessary services like email and active directory.

Here’s a great step-by-step guide from Chris Poteet describing how to set up a free email server for testing and evaluating the email capabilities for SharePoint 2010:

Using hMailServer with SharePoint.