1,804 articles and 14,599 comments as of Tuesday, February 22nd, 2011

EndUserSharePoint has combined resources with NothingButSharePoint.com. You can now find End User (Mark Miller), Developer (Jeremy Thake) and IT Pro SharePoint (Joel Oleson) content all in one place!

This site is a historical archive and is no longer being updated. Please update your favorites, bookmarks and RSS feeds.

NothingButSharePoint.com
Tuesday, April 21, 2009

JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)

Web services are hot. I may write a jQuery plugin to talk to SP web services at some point, but first I wanted to do something novel, write a JavaScript tool that can fetch list data using the RPC method (specifically the URL protocol).

Who Wants This?

Anyone developing solutions around List View web parts may find this helpful. A common request from users involves performing various math functions on calculated columns. OOB, SP does not do this which lead me to develop code like Total Calculated Columns.

Better Than DOM Walking?

The DOM walking technique I used previously works but has limitations. Pulling data out of the rendered HTML comes with a set of problems. First, since we read the DOM for data, we can only include rendered records. Records inside closed groups have not loaded and therefore cause errors in the calculations. Second, these scripts are fragile because the DOM can change. Users often have the ability to move (or remove) columns.

Better Than Web Services?

I would argue that the RPC method performs better than web services, but I don’t have any data to support that (yet). I know that the server caches responses to RPC requests and the web parts on the page have already performed the same request. So, we should get a response in the shortest time possible (about 110 ms on my test site).

The power of web services makes some admins nervous. If your server admins have taken precautions to limit or inhibit web services, owssvr.dll will still be an option–it must be because of the way SP works under the hood.

Lastly, through some hacks, owssvr.dll can do some tricks web services can not. For instance, fetching all items in a discussion list (folders/threads and messages/posts), as far as I know, can only be done with the RPC method.

Web services still holds one significant advantage over RPC, the ability to request a list by name. RPC must use the GUID.

Click “Read more…” to get the code and documentation:

Some Documentation

I created several helper functions which are part of the ows object. Each has a usage example in the myCb() callback function at the bottom.

When creating the ows object, the default settings will get the list and view GUIDs from the ctx object on the page. If there is more than one web part, the default settings use the ctx object for the first web part added to the page (not the first appearing in the DOM). To get the correct ctxId for a web part by name, see my other article.

If you specify a custom ctx, you will need to supply the list and view members of the options argument (see last example).

  • $.fn.ows(options,callback) – creates the ows object and returns an optional callback function with the modified options argument
  • ows.getName(ctx,rsname) – Takes the web part context (can be derived from the options argument if used in the callback) and a column by its display name and returns the ows field name (ows_)
  • ows.getNumber(ctx,rsname) – Takes the web part context and a column’s display name and returns the order number assigned to the field in the schema
  • ows.getValues(ctx,rsname) – Takes the web part context and a column’s display name and returns defined values found in the ows data
  • ows.getAll(ctx,rsname) – Takes the web part context and a column’s display name and returns all values from the ows data
  • ows.getSum(ctx,rsname) – Takes the web part context and a column’s display name and returns a sum of numerical, defined data
  • ows.getAvg(ctx,rsname) - Takes the web part context and a column’s display name and returns an average of defined values
  • ows.getMax(ctx,rsname) – Takes the web part context and a column’s display name and returns the maximum value
  • ows.getMin(ctx,rsname) – Takes the web part context and a column’s display name and returns the minimum value
  • ows.getTable(ctx) – Takes the web part context and returns the web part’s main table as a jQuery object
  • ows.getPosition(ctx,rsname) – Takes the web part context and a column’s display name and returns the order number – 1 (same as number but base 0)
<script type="text/javascript">
//?PageView=Shared&ToolPaneView=2
if(typeof jQuery=="undefined"){
	var jQPath="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/";
	document.write("<script src='",jQPath,"jquery.min.js' type='text/javascript'><\/script>");
}
</script>
<script type="text/javascript">
(function(){
	Array.max = function(array){
		return Math.max.apply( Math, array );
	};
	Array.min = function(array){
		return Math.min.apply( Math, array );
	};
	var defaults = {
		ctx: "ctx1",
		baseUrl: "",
		list: "",
		view: "",
		command: ""
	},
	isNumber = function(val){
		return (/^-?((\d+\.?\d*)|(\.\d+))$/).test(val);
	},
	ows_url = function(o){
		o.baseUrl = (!o.baseUrl)?L_Menu_BaseUrl:o.baseUrl;
		o.list = (!o.list)?window[o.ctx].listName:o.list;
		o.view = (!o.view)?window[o.ctx].view:o.view;
		return o.baseUrl+"/_vti_bin/owssvr.dll?XMLDATA=1&List="+
				o.list+"&View="+o.view+o.command;
	},
	create_ows = function(){
		ows = {
			getName : function(ctx,rsname){
				var n = $.grep($(ows[ctx].schema),function(e,i){
					return ($(e).attr("rs:name")==rsname);
				});
				return $(n).attr("name");
			},
			getNumber : function(ctx,rsname){
				var r = $.grep($(ows[ctx].schema),function(e,i){
					return ($(e).attr("rs:name")==rsname);
				});
				return $(r).attr("rs:number");
			},
			getPosition : function(ctx,rsname){
				var pos = ows.getNumber(ctx,rsname);
				return parseInt(pos)-1;
			},
			getValues : function(ctx,rsname){
				var v = [],
					vname = ows.getName(ctx,rsname);
				$(ows[ctx].data).each(function(i,e){
					var vx = $(e).attr(vname);
					if(vx){
						v.push(vx);
					}
				});
				return v;
			},
			getAll : function(ctx,rsname){
				var a = [],
					aname = ows.getName(ctx,rsname);
				$(ows[ctx].data).each(function(i,e){
					a.push($(e).attr(aname));
				});
				return a;
			},
			getSum : function(ctx,rsname){
				var svals = ows.getValues(ctx,rsname),
					sum=0;
				$(svals).each(function(i,e){
					if(e && isNumber(e)){
						sum += parseFloat(e);
					}
				});
				return sum;
			},
			getAvg : function(ctx,rsname){
				var avgsum = ows.getSum(ctx,rsname),
					avgcount = ows.getValues(ctx,rsname).length;
				return avgsum/avgcount;
			},
			getMax : function(ctx,rsname){
				var xvals = ows.getValues(ctx,rsname);
				xvals = $.grep(xvals,function(e,i){
					return (e && isNumber(e));
				});
				return Array.max(xvals);
			},
			getMin : function(ctx,rsname){
				var nvals = ows.getValues(ctx,rsname);
				nvals = $.grep(nvals,function(e,i){
					return (e && isNumber(e));
				});
				return Array.min(nvals);
			},
			getTable : function(ctx){
				var t = $("[id='"+window[ctx].listName+"-"+window[ctx].view+"']");
				return t;
			}
		};
	};
	jQuery.fn.ows = function(options,callback){
		var o = $.extend({},defaults,options);
		if(typeof ows=='undefined'){create_ows();}
		$.get(ows_url(o),function(data){
			ows[o.ctx] = {
				schema:$(data).find("[nodeName='s:AttributeType']"),
				data:$(data).find("[nodeName='z:row']")
			};
			return callback(o);
		});
	};
})();
$(function(){
	var myCb = function(o){
		var name = ows.getName(o.ctx,'% Complete');
		//console.log(name);
		var num = ows.getNumber(o.ctx,'% Complete');
		//console.log(num);
		var vals = ows.getValues(o.ctx,'% Complete');
		//console.log(vals);
		var all = ows.getAll(o.ctx,'% Complete');
		//console.log(all);
		var sum = ows.getSum(o.ctx,'% Complete');
		//console.log(sum);
		var count = ows[o.ctx].data.length;
		//console.log(count);
		var avg = ows.getAvg(o.ctx,'% Complete');
		//console.log(avg);
		var max = ows.getMax(o.ctx,'% Complete');
		//console.log(max);
		var min = ows.getMin(o.ctx,'% Complete');
		//console.log(min);
		var t = ows.getTable(o.ctx);
		//console.log(t);
		var pos = ows.getPosition(o.ctx,'% Complete');
		//console.log(pos);
	};
	//this example builds the ows object for the default web part and runs a callback function myCb
	//$.fn.ows({},myCb); 

	//this example builds the ows object for the default web part
	//$.fn.ows();

	//this example specifies the web part to use and uses an inline callback function
	$.fn.ows({ctx:'ctx1'},function(o){
		console.log(ows.getValues('ctx1','ows_PercentComplete'));
	});

	//this example specifies the list and view which does not need the web part on the page
	//$.fn.ows({ctx:"custom", list:"{730166FA-800A-4514-8912-AF49C681DF94}", view:"{2AB283B2-7B67-44AF-BC2E-304ACEED2035}"},myCb);
});
</script>

Thanks to John Resig for the min/max function idea.


Entries in this series:
  1. JQuery for Everyone: Accordion Left Nav
  2. JQuery for Everyone: Print (Any) Web Part
  3. JQuery for Everyone: HTML Calculated Column
  4. JQuery for Everyone: Dressing-up Links Pt1
  5. JQuery for Everyone: Dressing-up Links Pt2
  6. JQuery for Everyone: Dressing-up Links Pt3
  7. JQuery for Everyone: Cleaning Windows Pt1
  8. JQuery for Everyone: Cleaning Windows Pt2
  9. JQuery for Everyone: Fixing the Gantt View
  10. JQuery for Everyone: Dynamically Sizing Excel Web Parts
  11. JQuery for Everyone: Manually Resizing Web Parts
  12. JQuery for Everyone: Total Calculated Columns
  13. JQuery for Everyone: Total of Time Differences
  14. JQuery for Everyone: Fixing Configured Web Part Height
  15. JQuery for Everyone: Expand/Collapse All Groups
  16. JQuery for Everyone: Preview Pane for Multiple Lists
  17. JQuery for Everyone: Preview Pane for Calendar View
  18. JQuery for Everyone: Degrading Dynamic Script Loader
  19. JQuery for Everyone: Force Checkout
  20. JQuery for Everyone: Replacing [Today]
  21. JQuery for Everyone: Whether They Want It Or Not
  22. JQuery for Everyone: Linking the Attachment Icon
  23. JQuery for Everyone: Aspect-Oriented Programming with jQuery
  24. JQuery for Everyone: AOP in Action - loadTip Gone Wild
  25. JQuery for Everyone: Wiki Outbound Links
  26. JQuery for Everyone: Collapse Text in List View
  27. JQuery for Everyone: AOP in Action - Clone List Header
  28. JQuery for Everyone: $.grep and calcHTML Revisited
  29. JQuery for Everyone: Evolution of the Preview
  30. JQuery for Everyone: Create a Client-Side Object Model
  31. JQuery for Everyone: Print (Any) Web Part(s) Plugin
  32. JQuery for Everyone: Minimal AOP and Elegant Modularity
  33. JQuery for Everyone: Cookies and Plugins
  34. JQuery for Everyone: Live Events vs. AOP
  35. JQuery for Everyone: Live Preview Pane
  36. JQuery for Everyone: Pre-populate Form Fields
  37. JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)
  38. Use Firebug in IE
  39. JQuery for Everyone: Extending OWS API for Calculated Columns
  40. JQuery for Everyone: Accordion Left-nav with Cookies Speed Test
  41. JQuery for Everyone: Email a List of People with OWS
  42. JQuery for Everyone: Faster than Document.Ready
  43. jQuery for Everyone: Collapse or Prepopulate Form Fields
  44. jQuery for Everyone: Hourly Summary Web Part
  45. jQuery for Everyone: "Read More..." On a Blog Site
  46. jQuery for Everyone: Slick Speed Test
  47. jQuery for Everyone: The SharePoint Game Changer
  48. JQuery For Everyone: Live LoadTip
 

Please Join the Discussion

8 Responses to “JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)”
  1. Christophe says:

    Paul, I see that you mention caching in your post. Have you experienced issues where the list is updated but because of the cache the browser still returns the old result? Is there any way to tune the cache behavior for all users?

  2. AutoSponge says:

    @Christophe,

    Only IE8 seems to have this problem, but it can be reversed by changing from .get() to .ajax(). Using the AJAX method, set the cache option to false. This appends a random query string to the request so the browser will not cache it.

  3. Christophe says:

    I think I’ve seen this issue in previous versions of IE…well, nevermind.
    Thanks for reminding me of the tip about random querystrings :-) (I didn’t know it was used in jQuery btw).

  4. John says:

    Interest post but was not able to find the “Click “Read more…” to get the code and documentation:” – where can I see the code. Am especially interested in the call to owssvr.dll.

    Thanks,
    John

  5. John – Should be visible now. — Mark

  6. Has anyone ever tried to use jQuery to export data to Excel? For example, if I query a list via web service and display it in an HTML table, now, I’d like to export this same data to excel. Any idea on how I could do that?

Trackbacks

Check out what others are saying about this post...
  1. Getting XML Data From a SharePoint List – The Easy Way…

    Steve Pietrek has a great link blog ( Steve Pietrek – Everything SharePoint and Office ) that aggregates…

  2. [...] accomplish this, I did two things to my OWS API. First, I added a getType method that can check the schema’s datatype tag and return the type [...]




Notify me of comments to this article:


Speak and you will be heard.

We check comments hourly.
If you want a pic to show with your comment, go get a gravatar!