I need to use JSOM to copy a set of items (found from a CAML query) from one SharePoint list to another.
I'm pretty experienced doing this type of thing on the server side, but relatively new with it on the client side, with the async model. Based on my research, I need to use promises to do this, since I need to know when the copy is complete and whether or not any errors occurred.
I also need to copy the items one at a time, rather than batching them, because there can be a large number of items to copy, with many fields, and I run into message size errors from SharePoint if I try to do them all as one batch.
Below is a snippet of my code (with some irrelevant things removed). This appears to be working - except for one thing. As it does the copy of the items, I update a div on the page with the item number (just a counter) being copied as a progress indicator. Two things I notice:
The div is not updated with the counter sequentially. With the promise, I guess I expected to see
1, 2, 3, 4...but the numbers jump around, not sequential. Not a big deal, just not what I expected.This is more of an issue. In my code, I have a function
updateSuccessFinalthat I call after the.done(), which callsalert()to notify the user the copy is complete. However, while that alert is displayed, I can see thedivstill being updated with item numbers, meaning it is still doing the copying. I need this alert to only display when all of the items have been written. If the user dismisses the alert, there could be items that were not copied because they were still queued up.
Am I using the promise incorrectly in my code? How can I insure that the function updateSuccessFinal is not called until all items have been written?
I am using d = $.Deferred() inside my addItem function. I have also tried moving that outside the loop and passing d to addItem and that did not seem to make any difference.
function snapshotItems(sourceListName, queryText, targetListName) { // Snapshot all of the items picked up in the criteria of view view into the snapshot list var context = new SP.ClientContext.get_current(); var srcList = context.get_web().get_lists().getByTitle(sourceListName); // Create query var query = new SP.CamlQuery(); query.set_viewXml(queryText); // Items matching the query var items = srcList.getItems(query); context.load(items); // Insert the rows in the Snapshot table context.executeQueryAsync( function() { var listEnumerator = items.getEnumerator(); var i = 0; while (listEnumerator.moveNext()) { var curListItem = listEnumerator.get_current(); addItem(curListItem, i).done( function() { } ); i++; } snapshotSuccessFinal(); }, function(sender, args) { alert('Get snapshot query failed: ' + args.get_message()); } ); function addItem(curListItem, i) { // Need to use promises because batching all the adds results in a "message too large" error from SharePoint // So uses promises to execute one add at a time var d = $.Deferred(); var context = new SP.ClientContext.get_current(); var targetList = context.get_web().get_lists().getByTitle(targetListName); var itemCreateInfo = new SP.ListItemCreationInformation(); var listItem = targetList.addItem(itemCreateInfo); // Copy the custom fields listItem.set_item("Title", curListItem.get_item("Title")); // Write it listItem.update(); context.load(listItem); var passedData = { dfd: d, idx: i }; context.executeQueryAsync(Function.createDelegate(passedData,onUpdateOneSuccess), Function.createDelegate(passedData,onUpdateOneFailed)); return d.promise(); } function onUpdateOneSuccess() { $("workingDiv").text("Snapshot item #" + this.idx.toString()); this.dfd.resolve(); } function onUpdateOneFailed() { alert("Add item failed: " + args.get_message()) } } function snapshotSuccessFinal() { $("workingDiv").text("Snapshot populated."); alert("Snapshot created successfully."); }