sharepointsharepoint-onlinecsomsharepoint-jsom

SharePoint Columns Don't Update After Successful JSOM update() call


I have a workflow that copies items from a standard Calendar list to a second standard Calendar list. But the copies contain less details about any given calendar item than the original. This works just fine except that the Start Time and End Time fields get copied over in UTC time and we need those times displayed in the user's local time zone (we have employees all over the world, so it can't be a static adjustment). I am aware that SharePoint stores all Dates/Times as UTC and I get why.

My solution to this problem is to (in the main calendar) create two hidden Date/Time columns that I would populate with the dates/times from the EventDate and EndDate Calendar columns (the UTC values), but these new values would be corrected for the time zone offset using the SharePoint Client Side Object Model (using JS). Then my workflow would copy these corrected dates/times over to the second calendar.

The following code appears to work. My success handlers are getting called for each iteration of the list items and my logged output shows all the correct values. However, after the code runs, the two Date fields that are supposed to have been updated/set on each item by the code remain blank.

<script src="/_layouts/15/sp.runtime.js"></script> 
<script src="/_layouts/15/sp.js"></script> 
<script>
var siteUrl = "https://duckcreek.sharepoint.com/sites/DCU";

function loadListItems(siteUrl) {
  var ctx = new SP.ClientContext(siteUrl);
  var oList = ctx.get_web().get_lists().getByTitle("Master Calendar");
  var camlQuery = new SP.CamlQuery();
  this.collListItem = oList.getItems(camlQuery);

  // EventDate and EndDate are the pre-defined fields (from the Event  
  // content type) that represent the event start and end date / times
  // CorrectedStartDateTime and CorrectedEndDateTime are pre-existing 
  // Date / Time fields in the list.
  ctx.load(collListItem,
    "Include(Id, DisplayName, EventDate, EndDate, CorrectedStartDateTime, CorrectedEndDateTime)");
  ctx.executeQueryAsync(
    Function.createDelegate(this, this.onLoadListItemsSucceeded),
    Function.createDelegate(this, this.onLoadListItemsFailed));
}

function onLoadListItemsSucceeded(sender, args) {
  var ctx = new SP.ClientContext(siteUrl);
  var oList = ctx.get_web().get_lists().getByTitle("Master Calendar");
  var listItemInfo = "";
  var listItemEnumerator = collListItem.getEnumerator();

  while (listItemEnumerator.moveNext()) {
    // List is loaded. Enumerate the list items
    let oListItem = listItemEnumerator.get_current();

    // Get the Calendar item Start and End dates as stored in SharePoint (UTC)
    let startDateString = oListItem.get_item("EventDate").toString();
    let endDateString = oListItem.get_item("EndDate").toString();

    // Get the GMT time zone offset
    let startDateOffsetHours = parseInt(startDateString.split("GMT-")[1]) / 100; 
    let endDateOffsetHours = parseInt(endDateString.split("GMT-")[1]) / 100; 

    // Create new dates that are the same as the originals
    let resultStartDate = new Date(startDateString);
    let resultEndDate = new Date(endDateString);

    // Adjust the new dates to be correct for the local time zone based on the UTC offset
    resultStartDate.setHours(resultStartDate.getHours() + startDateOffsetHours);
    resultEndDate.setHours(resultEndDate.getHours() + endDateOffsetHours);

    // Update the two placeholder fields in the current list item with the corrected dates
    oListItem.set_item("CorrectedStartDateTime", resultStartDate);
    oListItem.set_item("CorrectedEndDateTime", resultEndDate);

    // Update SharePoint
    oListItem.update();
    ctx.executeQueryAsync(
      Function.createDelegate(this, this.onSetCorrectDateTimesSucceeded),
      Function.createDelegate(this, this.onSetCorrectDateTimesFailed)
    );

    // This is just for diagnostics, but it does show the correct data.
    // And since we are using .get_item() here, it would seem that we
    // are pulling the correct data out of SharePoint, but the two "Corrected"
    // fields remain empty after this function completes successfully.
    listItemInfo += "\nDisplay name: " + oListItem.get_displayName() +
      "\n\tEventDate: " + oListItem.get_item("EventDate") +
      "\n\tEndDate: " + oListItem.get_item("EndDate") +
      "\n\t\tCorrectedStartDateTime: " + oListItem.get_item("CorrectedStartDateTime") +
      "\n\t\tCorrectedEndDateTime: " + oListItem.get_item("CorrectedEndDateTime") +
      "\n--------------------------------------------------------------------------------";
  }
  console.log(listItemInfo.toString());
}

function onLoadListItemsFailed(sender, args) {
  console.log("Request failed!" + args.get_message() + "\n" + args.get_stackTrace());
}

function onSetCorrectDateTimesSucceeded() {
  console.log("Item updated!");
}

function onSetCorrectDateTimesFailed(sender, args) {
  console.log("Request failed!" + args.get_message() + "\n" + args.get_stackTrace());
}

loadListItems(siteUrl); 

</script>​​​

And, here is one of the records that the code produces (it actually produces one of the following for each of the items on the first calendar as it should):

Display name: NEW TEST
    EventDate: Mon Dec 31 2018 19:00:00 GMT-0500 (Eastern Standard Time)
    EndDate: Tue Jan 01 2019 18:59:00 GMT-0500 (Eastern Standard Time)
        CorrectedStartDateTime: Tue Jan 01 2019 00:00:00 GMT-0500 (Eastern Standard Time)
        CorrectedEndDateTime: Tue Jan 01 2019 23:59:00 GMT-0500 (Eastern Standard Time)
--------------------------------------------------------------------------------

However, the calendar items don't get the two "corrected" fields updated:

enter image description here


Solution

  • Simple answer

    You recreate the SP.ClientContext in the onLoadListItemsSucceeded function, so you don't actually call update for the original list item. Save it in a variable and reuse it instead.

    Complete code

    <script src="/_layouts/15/sp.runtime.js"></script> 
    <script src="/_layouts/15/sp.js"></script> 
    <script>
    var siteUrl = "https://duckcreek.sharepoint.com/sites/DCU";
    var ctx;
    
    function loadListItems(siteUrl) {
      ctx = new SP.ClientContext(siteUrl);
      var oList = ctx.get_web().get_lists().getByTitle("Master Calendar");
      var camlQuery = new SP.CamlQuery();
      this.collListItem = oList.getItems(camlQuery);
    
      // EventDate and EndDate are the pre-defined fields (from the Event  
      // content type) that represent the event start and end date / times
      // CorrectedStartDateTime and CorrectedEndDateTime are pre-existing 
      // Date / Time fields in the list.
      ctx.load(collListItem,
        "Include(Id, DisplayName, EventDate, EndDate, CorrectedStartDateTime, CorrectedEndDateTime)");
      ctx.executeQueryAsync(
        Function.createDelegate(this, this.onLoadListItemsSucceeded),
        Function.createDelegate(this, this.onLoadListItemsFailed));
    }
    
    function onLoadListItemsSucceeded(sender, args) {
      var oList = ctx.get_web().get_lists().getByTitle("Master Calendar");
      var listItemInfo = "";
      var listItemEnumerator = collListItem.getEnumerator();
    
      while (listItemEnumerator.moveNext()) {
        // List is loaded. Enumerate the list items
        let oListItem = listItemEnumerator.get_current();
    
        // Get the Calendar item Start and End dates as stored in SharePoint (UTC)
        let startDateString = oListItem.get_item("EventDate").toString();
        let endDateString = oListItem.get_item("EndDate").toString();
    
        // Get the GMT time zone offset
        let startDateOffsetHours = parseInt(startDateString.split("GMT-")[1]) / 100; 
        let endDateOffsetHours = parseInt(endDateString.split("GMT-")[1]) / 100; 
    
        // Create new dates that are the same as the originals
        let resultStartDate = new Date(startDateString);
        let resultEndDate = new Date(endDateString);
    
        // Adjust the new dates to be correct for the local time zone based on the UTC offset
        resultStartDate.setHours(resultStartDate.getHours() + startDateOffsetHours);
        resultEndDate.setHours(resultEndDate.getHours() + endDateOffsetHours);
    
        // Update the two placeholder fields in the current list item with the corrected dates
        oListItem.set_item("CorrectedStartDateTime", resultStartDate);
        oListItem.set_item("CorrectedEndDateTime", resultEndDate);
    
        // Update SharePoint
        oListItem.update();
        ctx.executeQueryAsync(
          Function.createDelegate(this, this.onSetCorrectDateTimesSucceeded),
          Function.createDelegate(this, this.onSetCorrectDateTimesFailed)
        );
    
        // This is just for diagnostics, but it does show the correct data.
        // And since we are using .get_item() here, it would seem that we
        // are pulling the correct data out of SharePoint, but the two "Corrected"
        // fields remain empty after this function completes successfully.
        listItemInfo += "\nDisplay name: " + oListItem.get_displayName() +
          "\n\tEventDate: " + oListItem.get_item("EventDate") +
          "\n\tEndDate: " + oListItem.get_item("EndDate") +
          "\n\t\tCorrectedStartDateTime: " + oListItem.get_item("CorrectedStartDateTime") +
          "\n\t\tCorrectedEndDateTime: " + oListItem.get_item("CorrectedEndDateTime") +
          "\n--------------------------------------------------------------------------------";
      }
      console.log(listItemInfo.toString());
    }
    
    function onLoadListItemsFailed(sender, args) {
      console.log("Request failed!" + args.get_message() + "\n" + args.get_stackTrace());
    }
    
    function onSetCorrectDateTimesSucceeded() {
      console.log("Item updated!");
    }
    
    function onSetCorrectDateTimesFailed(sender, args) {
      console.log("Request failed!" + args.get_message() + "\n" + args.get_stackTrace());
    }
    
    loadListItems(siteUrl); 
    
    </script>​​​
    

    Additional notes