0

I have a query against a sharepoint list that returns some data, but then for each item I have to make another query to get its document type (content type), the problem is that this part of the code is executed after the page has been rendered.

var cname = getContentTypeOfCurrentItem(listItemValues['ID'].toString());
listItemsWithValues['Document Type'] = cname;


function GetRelatedBillingDocumentsFromList(selectProperties, currentBillCyclePath, clientCode, jobCodes, engagementCode, enhanceFunctions) {
  $log.info('Retrieving related billing documents for bill cycle with name [' + currentBillCyclePath + ']');
  var deferred = $q.defer();
  var webUrl = _spPageContextInfo.webAbsoluteUrl;
  var viewFields = spService.ConvertSelectPropertiesToViewFields(selectProperties);
  // query must return the documents for the same client but in other bill cycles not the current one
  var camlQuery = '<View Scope="RecursiveAll">' + viewFields +
    '<Query>' +
    '<Where>' +
    '<And>' +
    '<Eq>' +
    '<FieldRef Name="ClientCode" />' +
    '<Value Type="Text">' + clientCode + '</Value>' +
    '</Eq>' +
    '<Neq>' +
    '<FieldRef Name="ContentType" />' +
    '<Value Type="Computed">Bill Cycle</Value>' +
    '</Neq>' +
    '</And>' +
    '</Where>' +
    '</Query>' +
    '</View>';

  var billCyclesListId = '{c23bbae4-34f7-494c-8f67-acece3ba60da}';
  spService.GetListItems(billCyclesListId, camlQuery, selectProperties)
    .then(function (listItems) {
      var listItemsWithValues = [];

      if (listItems) {
        var enumerator = listItems.getEnumerator();
        var promises = [];
        while (enumerator.moveNext()) {
          var listItem = enumerator.get_current();
          var listItemValues = [];
          selectProperties
            .forEach(function (propertyName) {
              var value = listItem.get_item(propertyName);
              if (propertyName === 'PwC_JobCodesMulti') {
                jobvalue = '';
                value.forEach(function (jobvalues) {
                  jobvalue += jobvalues.get_lookupValue() + ';';
                });
                listItemValues[propertyName] = jobvalue;
              } else {
                listItemValues[propertyName] = value;
              }
              //listItemValues[propertyName] = value;
            });

          listItemsWithValues.push(listItemValues);
        }

        var cname = getContentTypeOfCurrentItem(listItemValues['ID'].toString());
        listItemsWithValues['Document Type'] = cname;
      }

      listItemsWithValues.forEach(function (listItem) {
        var fileDirRef = listItem['FileRef'];
        var id = listItem['ID'];
        var title = listItem['Title'];
        var serverUrl = _spPageContextInfo.webAbsoluteUrl.replace(_spPageContextInfo.webServerRelativeUrl, '');
        var dispFormUrl = serverUrl + '/sites/billing/_layouts/15/DocSetHome.aspx?id=' + fileDirRef;
        //listItem["FileRef"] = dispFormUrl;
        //listItem["Bill Cycle"] = dispFormUrl;

        var parentLink = listItem['FileRef'];
        arrayofstrings = parentLink.split('/');
        var billCycleFolderName = arrayofstrings[arrayofstrings.length - 2];
        arrayofstrings.pop();
        var hyperLink = '<a href="' + arrayofstrings.join('/') + '">' + billCycleFolderName + '</a>';
        listItem['Bill Cycle'] = hyperLink;

      });

      var enhancedListItemValues = spService.SpSearchQuery.EnhanceSearchResults(listItemsWithValues, enhanceFunctions);
      deferred.resolve(listItemsWithValues);
    })
    .catch(function (message) {
      deferred.reject();
    });

  return deferred.promise;
}

function getContentTypeOfCurrentItem(id) {

  var clientContext = new SP.ClientContext.get_current();
  var oList = clientContext.get_web().get_lists().getByTitle('Bill Cycles');
  listItem = oList.getItemById(id);
  clientContext.load(listItem);
  listContentTypes = oList.get_contentTypes();
  clientContext.load(listContentTypes);
  clientContext.executeQueryAsync(
    Function.createDelegate(this, getContentTypeOfCurrentItemSucceeded),
    function (error, errorInfo) {
      $log.warn('Retrieving list item result failed');
      deferred.reject(errorInfo);
    }
  );
}

function getContentTypeOfCurrentItemSucceeded(sender, args) {
  var ctid = listItem.get_item('ContentTypeId').toString();
  var ct_enumerator = listContentTypes.getEnumerator();
  while (ct_enumerator.moveNext()) {
    var ct = ct_enumerator.get_current();
    if (ct.get_id().toString() == ctid) {
      var contentTypeName = ct.get_name();
      return contentTypeName;
    }
  }
}

How do I chan promises here to make sure that the content type call is done right?

str
  • 42,689
  • 17
  • 109
  • 127
Luis Valencia
  • 32,619
  • 93
  • 286
  • 506
  • 3
    [You don't need `$q.defer()` when you already have a promise](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it). – str Dec 11 '17 at 08:48
  • Avoid the [deferred antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Dec 11 '17 at 11:28
  • So there is an asynchronous query at the `getContentTypeOfCurrentItem`. I suppose you would like to do multiple amount of asynchronous queries in the `EnhanceSearchResults` and return that result? – jervtub Dec 17 '17 at 00:30
  • The question is not clear. Do you want to run the promises in parallel? Can you explain your intent more clearly? – Pubudu Dodangoda Dec 20 '17 at 07:23

1 Answers1

2

I've refactored your example to understand the intent of your code. I think that you need to understand better the async nature of promises and the idea of scope / closures (I don't think your code works at all, in many ways, i've included a review in the oldscript.js file).

https://embed.plnkr.co/3YcHZzxH4u6ylcA2lJZl/

My example uses a Stub object to simulate your provider, but to keep it short I'd say: the key is Promise.all, a factory that generates a new Promise that gets fulfilled when all the promises are resolved.

You need to store one promise for each item holding the future value for each ID (your original snippet only stores the last ID, it seemed to be a bug), and when all of them are resolved you can keep working with your data (later on time, aka async or deferred).

Hope it helps


A relevant snippet...

function addContentType(listItem){
  var promise = getContentTypeOfCurrentItem(listItem.ID.toString());
  promise.then(function(cname){
    listItem['Document Type'] = cname;
  });
  return promise;
}

function processListItems(listItems) {
var listItemsWithValues = listItems.map(listItemToValue);

var promises = listItemsWithValues.map(addContentType);
Promise.all(promises).then(youCanUseTheData);

function youCanUseTheData(){
  /*
  At this point, each listItem holds the 'Document Type' info
  */
  listItemsWithValues.forEach(function (listItem) {
    console.log(listItem);
  });
}
sminutoli
  • 853
  • 4
  • 11
  • 2
    Please post the relevant code in your answer, not just a link to it. – Bergi Dec 18 '17 at 06:37
  • I needed to refactor / mock a lot of code to understand better (and test that's working), the original code contained various bugs, I've added a snippet but you'll need to dig on the plunk to understand it in context – sminutoli Dec 18 '17 at 06:53