Wednesday, October 31, 2007

What We’ve Learned about AJAX Code in SharePoint

In “Inside WSS” we did a chapter on using ASP.NET AJAX technologies within SharePoint. There are a few things we’ve learned since writing the book that we’d like to clear up, and a few ASP.NET techniques that we’d recommend avoiding in favor of more stable methods, as well as some additional tips and tricks for coding AJAX applications in the portal. We’ve been proving these technologies both in the classroom and through commercial applications for close to a year now, refined some methods and established the supportable development story. I'll add this to an official errata for the book, but these tips will be useful to any ajax developer working in SharePoint.
First off, we would not recommend using the WebResource and ScriptResource technologies. We talk about this in the Web Parts chapter as well as the Ajax chapter, but in commercial applications we’ve decided to deploy resources to the “_layouts” vdir which is easier to debug and avoids a bug with the SharePoint runtime, in which the call to compiled script resources sometimes fails when accessed by a non-administrator. This can be a difficult bug to track down in your application, especially if you develop as an administrative account on your dev box (but, something you can totally avoid when using Windows server 2008 as your dev box!) What we do recommend and what is fully supported is deploying resources to a folder for your application in the Layouts application (http://localhost/_layouts/[your app name].)
So instead of the following code:

string xsl = this.Page.ClientScript.GetWebResourceUrl(typeof(FeedListWebPart),@"LitwareAjaxWebParts.Resources.OPML.xslt");this.XsltUrl = SPContext.Current.Web.Url + @"/" + xsl;

We would simply use:

this.XsltUrl = @"/_layouts/litware/rss.xslt";
ASP.NET AJAX ScriptReferences are simplified as well. Instead of compiling in the resource and adding the required assembly attribute before adding the script reference by fully qualified type name, we recommend adding a script reference with the URL:

this.AtlasScriptManager.Scripts.Add(new ScriptReference("/_layouts/Litware/ListViewWebPart.js"));
Another point of failure we've found is the ASP.NET AJAX JavaScript proxy generator. We've found that in most scenarios, the reference to foo.asmx/js fails when the calling user is not the administrator. So instead of creating a ServiceReference with the ASP.NET Script Manager, we instead save the asmx/js endpoint to a JavaScript file that we deploy with our other scripts. When calling web services that use the site context, it is also necessary to set the proxy's url before each call, or you'll be calling the webservice from the root web site's context. Because of this, we include the spWebUrl property in the SharePoint Ajax Toolkit, which writes the current SPWeb's Url to the window.spWebUrl property. To do this, use the set_path method of your web service proxy, like this, immediately before the call:

Litware.WikiWebService.set_path(window.spWebUrl+'/_vti_bin/Litware/WikiWebService.asmx');
Another thing that we’ve done in the SharePoint Ajax Toolkit is add a cache key, which is generated on each build. You can then append this cache key to script resources, or even output it as JavaScript to use on the client, to ensure that scripts and xslt resources are cached (and NOT cached) properly between builds. You really don’t want to have to tell your users to clear their cache after each load—that’s just embarrassing! The following code will generate an arbitrary string to append in a query for each url, which will cause the browser to cache the resource uniquely per build.
public readonly static string CacheKey = Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId.GetHashCode().ToString(CultureInfo.InvariantCulture);
You would then use this in code like this: this.XsltUrl = @"/_layouts/litware/rss.xslt?" + Utility.CacheKey;
We've also added many refinements to the SharePoint AJAX Toolkit at NewsGator in our product lifecycles, so be sure to grab the latest code from www.codeplex.com/sharepointajax.

No comments: