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>
- JQuery for Everyone: Accordion Left Nav
- JQuery for Everyone: Print (Any) Web Part
- JQuery for Everyone: HTML Calculated Column
- JQuery for Everyone: Dressing-up Links Pt1
- JQuery for Everyone: Dressing-up Links Pt2
- JQuery for Everyone: Dressing-up Links Pt3
- JQuery for Everyone: Cleaning Windows Pt1
- JQuery for Everyone: Cleaning Windows Pt2
- JQuery for Everyone: Fixing the Gantt View
- JQuery for Everyone: Dynamically Sizing Excel Web Parts
- JQuery for Everyone: Manually Resizing Web Parts
- JQuery for Everyone: Total Calculated Columns
- JQuery for Everyone: Total of Time Differences
- JQuery for Everyone: Fixing Configured Web Part Height
- JQuery for Everyone: Expand/Collapse All Groups
- JQuery for Everyone: Preview Pane for Multiple Lists
- JQuery for Everyone: Preview Pane for Calendar View
- JQuery for Everyone: Degrading Dynamic Script Loader
- JQuery for Everyone: Force Checkout
- JQuery for Everyone: Replacing [Today]
- JQuery for Everyone: Whether They Want It Or Not
- JQuery for Everyone: Linking the Attachment Icon
- JQuery for Everyone: Aspect-Oriented Programming with jQuery
- JQuery for Everyone: AOP in Action - loadTip Gone Wild
- JQuery for Everyone: Wiki Outbound Links
- JQuery for Everyone: Collapse Text in List View
- JQuery for Everyone: AOP in Action - Clone List Header
- JQuery for Everyone: $.grep and calcHTML Revisited
- JQuery for Everyone: Evolution of the Preview
- JQuery for Everyone: Create a Client-Side Object Model
- JQuery for Everyone: Print (Any) Web Part(s) Plugin
- JQuery for Everyone: Minimal AOP and Elegant Modularity
- JQuery for Everyone: Cookies and Plugins
- JQuery for Everyone: Live Events vs. AOP
- JQuery for Everyone: Live Preview Pane
- JQuery for Everyone: Pre-populate Form Fields
- JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)
- Use Firebug in IE
- JQuery for Everyone: Extending OWS API for Calculated Columns
- JQuery for Everyone: Accordion Left-nav with Cookies Speed Test
- JQuery for Everyone: Email a List of People with OWS
- JQuery for Everyone: Faster than Document.Ready
- jQuery for Everyone: Collapse or Prepopulate Form Fields
- jQuery for Everyone: Hourly Summary Web Part
- jQuery for Everyone: "Read More..." On a Blog Site
- jQuery for Everyone: Slick Speed Test
- jQuery for Everyone: The SharePoint Game Changer
- JQuery For Everyone: Live LoadTip
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!
@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!
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.)
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
@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.
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:
@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.