1,804 articles and 14,851 comments as of Wednesday, May 18th, 2011

EndUserSharePoint has combined resources with NothingButSharePoint.com. You can now find End User (Mark Miller), Developer (Jeremy Thake) and IT Pro SharePoint 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
Wednesday, May 6, 2009

JQuery for Everyone: Extending OWS API for Calculated Columns

Last night, I wanted to sum a calculated column with groups on the page with the groups closed. SharePoint does not do this OOB and my previous attempt at a solution needed data rendered (groups expanded).

To 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 attribute. Next, I upgraded the getValues method to check type for “variant” and return the appropriate value. In the OWS world, calculated columns are called variant data types and they are stored with a type;#value syntax.

Since all of the other calculations rely on getValues, I don’t need to change anything else. Now I can call my API and write the sum of my calculated column in like three lines:

$(function(){
	//example on a custom Tasks list with grouping
	$.fn.ows({},function(o){
		var p = ows.getPosition(o.ctx,'calcNumbers');
		var s = ows.getSum(o.ctx,'calcNumbers');
		$("#aggr > tr > td:not(.ms-vh-group):eq("+p+")").html("<b>Sum = "+s+"</b>");
	});
});

The above script calls OWS to build the ows object. In the callback function, I use the getPosition and getSum methods to get a value and where it should be written. Since I’m using a list view, all of the CTX information is already on the page, I don’t need any custom options. The last line is an array of td’s in the aggregation row (#aggr) excluding the grouping column. The :eq() filter allows me to pick one of the array members by position (same as the schema position, hence why I can just ask OWS what the position is).

Click “Read more…” to get the updated OWS API and this example. To configure your list for this example, create a calculated column in a standard Task list called “calcNumbers”. Use a simple calculation like =[% Complete]*10. Set at least one row to use a total function (like Count), this creates the aggr row. Turn on grouping if you like, it should work either way.

<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");
			},
			getType : function(ctx,rsname){
				var n = $.grep($(ows[ctx].schema),function(e,i){
					return ($(e).attr("rs:name")==rsname);
				});
				return $(n).find("[nodeName='s:datatype']").attr("dt:type");
			},
			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){
						if(ows.getType(ctx,rsname)=='variant'){
							v.push(vx.split(';#')[1]);
						}else{
							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(){
	//example on a custom Tasks list with grouping
	$.fn.ows({},function(o){
		var p = ows.getPosition(o.ctx,'calcNumbers');
		//console.log(p);
		var s = ows.getSum(o.ctx,'calcNumbers');
		$("#aggr > tr > td:not(.ms-vh-group):eq("+p+")").html("<b>Sum = "+s+"</b>");
	});
});
</script>




Paul Grenier

View all entries in this series: PaulGrenier-JQuery for Everyone»
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

7 Responses to “JQuery for Everyone: Extending OWS API for Calculated Columns”
  1. Aaron says:

    How could you convert this to work with multiple lists on the same web part page? When I attempted, I changed the CTX default value to select the correct data source but the results still display on the first webpart on the page only. The code is excellent btw!

  2. AutoSponge says:

    @Aaron,

    If you used the getTable method to find the web part’s ID, make sure you set the right context on that too.

    Check my codeplex project for more examples and updated code: http://owsapi.codeplex.com/

    Glad you like it!

  3. Aaron says:

    I realized that I was missing the ListID and the ViewID from the web parts. I was wondering if it is possible to port these in from an array, similar to: (I’m using this to build cross-site google chart api’s, trying to aggregate all of the data to one request instead of separate.)

    $(function(){
         var listArr = ['{ctx:"ctx2",list:"{08F609C4-5643-4E7B-B771-CF61E98C9DFA}",view:"{0F6C8393-BABA-41C7-BEC7-F8E072529F22}"}'];
    
    $.each(listArr,function(index, ls){
      $.fn.ows(ls,function(o){
       var s = ows.getSum(o.ctx,'Points').toFixed(1);
       $("#jChart1").append(BuildGoogleChart(s));
    	});
       });
    });
    

    Using this, it does not appear to pull data, so I’m sure I’m doing something wrong. It does work one site at a time, I’d like to do more so that I could rank the results with an array sort.

    Thanks,
    Aaron

  4. AutoSponge says:

    @Aaron,

    You’re on the right track but you have to call the $.ows for each separate context (list/view).

    The way you have it, there is no list defined for the second set.

    I have a dynamic example on my codeplex project at owsapi.codeplex.com. It may help to put the list views on the page, hide them, and pull the guids dynamically.

  5. Aaron says:

    The problem that I seem to be having is that the value returned is undefined. From what I’ve found by googling, there seems to be an issue with returning the value of a nested function. I’m not sure if there is a way around that or not, I’m still searching..

    Here is the code snippet that I’m currently working with:

    function BuildGoogleChart(Data)
        {
        var  DataPoints= "&chd=t:"+Data;  
    
    return("");
        }
    
    function GetX(){
    $.fn.ows({ctx:"ctx2",list:"{C510C8F8-F624-46FA-8F55-8690C365B183}",view:"{CFF21CF7-9CA7-4CE8-AD08-F8A20BF31345}"}, function(o){
    		var r = ows.getSum(o.ctx,'Points').toFixed(1);
                      $("#jLoadMe1").append(""+r+"");  //control to ensure data is retrieved.
    
    	        return r;
    
    		});}
    
    function GetY(){
    
    $.fn.ows({ctx:"ctx2",list:"{08F609C4-5643-4E7B-B771-CF61E98C9DFA}",view:"{0F6C8393-BABA-41C7-BEC7-F8E072529F22}"}, function(o){
    		 s = ows.getSum(o.ctx,'Points').toFixed(1);
                      $("#jLoadMe1").append(""+s+"");  //control to ensure data is retrieved.
    
                     return s; 
    
                     });
    
    }
    
    function BuildGoogleData(sData)
       {
    	$("#jLoadMe1").append(BuildGoogleChart(sData));
       }
    
    $(function(){
    var xData = GetX();
    var yData = GetY();
    var cardArr = new Array(xData,yData);
    var cardSrt = cardArr.sort();
    BuildGoogleData(cardSrt);
    }); 
    
  6. AutoSponge says:

    @Aaron,

    The only error I see, is you named ctx2 twice. Since owsapi only ever constructs one object, in the ows object, there will only be one context, ctx2. Try naming one of them something else.

Trackbacks

Check out what others are saying about this post...
  1. [...] I needed to upgrade my OWS API with some new functions. I originally used .getValues() to get the values from a field and only [...]




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!