0

this is a kind of embarrassing question but I'm stuck. My background is managed code and I never learned JavaScript but yet I want to implement a tiny project. The script is running on SharePoint 2010, queries items from a custom list using the JavaScript Object Model and populates a Google chart or table respectively. With the help of MSDN and Google Developer I was able to query data from one list and visualize it.

However, I'm unable to transfer the concept to query multiple lists, combine result sets and finally pass to Google API. In my code I created a chain of callbacks like showChart->loadListData->drawChart. This proves to be bad design since it's inflexible and cannot be extended. All API methods are asynchronous and don't have return values but expect method names to call once finished. This is what get's me stuck and where I lack knowledge.

I'm very happy for every comment and answer, also I can provide actual source code if requested. Thank you in advance, Toby

UPDATE as asked for by @Utkanos:

var listItems;
$(document).ready(function() {      
    ExecuteOrDelayUntilScriptLoaded(loadChartData, "sp.js");
});
function loadChartData() {
    var camlQuery = SP.CamlQuery.createAllItemsQuery();
    camlQuery.set_viewXml("<View><Query><Where><Eq><FieldRef Name='Year'/><Value Type='Text'>2015</Value></Eq></Where></Query></View>");
    loadListData('CustomList', camlQuery, drawChart, readListItemFailed);
}
function loadListData(listTitle, camlQuery, onSuccess, onFail) { 
    context = SP.ClientContext.get_current();
    var list = context.get_web().get_lists().getByTitle(listTitle);
    var listItems = list.getItems(camlQuery);
    context.load(listItems);
    context.executeQueryAsync(function(sender, args){onSuccess(listItems);}, onFail);
}
function drawDpOverviewChart(listItems) {
    var data;
    var enumerator = listItems.getEnumerator();
    data = new google.visualization.DataTable();
    data.addColumn('string', 'Column1');
    data.addColumn('number', 'Column2');
    var listItem;
    while (enumerator.moveNext()) {
            listItem = enumerator.get_current();
            data.addRow([listItem.get_item('Title'), Math.round(listItem.get_item('Balance')/10000)/100]);
    }
    var options = {'title':'Pretty Chart'};
    var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
    chart.draw(data, options);
}
function readListItemFailed(sender, args) {
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
Toby
  • 570
  • 2
  • 8
  • 23
  • Please post your code. – Mitya Sep 22 '16 at 10:18
  • Possible duplicate of [How can I wait for set of asynchronous callback functions?](http://stackoverflow.com/questions/10004112/how-can-i-wait-for-set-of-asynchronous-callback-functions) – JJJ Sep 22 '16 at 10:18
  • if you're lists have lookup fields to one another you can use the [REST API](https://msdn.microsoft.com/en-us/library/ff798339.aspx) to "join" these in a single call... – WhiteHat Sep 22 '16 at 11:47

1 Answers1

0

if using SP 2010 on a typical .aspx page, you have some tools available,

such as MicrosoftAjax.js and _spPageContextInfo

using the REST API, you can join lists on lookup fields and include fields from both lists in one query

following is an example url for a call to rest...

'/_vti_bin/ListData.svc/MI_Projects?$expand=ModifiedBy&$filter=ModifiedBy/Id eq 738'

this call actually "joins" the list MI_Projects to the UserInformationList by "expanding" ModifiedBy

so when the data returns, you can access any of the user info fields, i.e.
row.ModifiedBy.Name

this can be done with lookup fields on custom lists as well

to make the call, you can use the Sys.Net.WebRequest class from MicrosoftAjax
this class also allows you to pass variables to the callback

see following snippet...

function makeCall() {

  // Sys.Net.WebRequest is from MicrosoftAjax.js
  var webRequest = new Sys.Net.WebRequest();
  webRequest.get_headers()['Cache-Control'] = 'no-cache';
  webRequest.get_headers()['Accept'] = 'application/json';
  webRequest.get_headers()['Content-Type'] = 'application/json';
  webRequest.set_url(_spPageContextInfo.webServerRelativeUrl + '/_vti_bin/ListData.svc/MI_Projects?$expand=ModifiedBy&$filter=ModifiedBy/Id%20eq%20738');

  // use the 'user context' to pass variables you want available in the callback
  webRequest.set_userContext({
    Title: 'variable to pass to completed callback'
  });
  webRequest.add_completed(restComplete);
  webRequest.invoke();

}

// the first argument of callback is the Sys.Net.WebRequestExecutor class
function restComplete(executor, eventArgs) {
  if (executor.get_responseAvailable()) {
    if (executor.get_statusCode() === 200) {

      // get variable passed via user context
      var variablePassed = executor.get_webRequest().get_userContext().Title;

      // i.e. -- build google table
      // add rows received from rest (forEach is from MicrosoftAjax.js)
      // list results array = executor.get_object().d.results

      Array.forEach(executor.get_object().d.results, function (row) {

        data.addRow(row.Title, row.Id, row.ModifiedBy.Name);

      }, this);
    }
  }
}
WhiteHat
  • 59,912
  • 7
  • 51
  • 133