I'm trying to use a script to remove multiple list items from multiple lists. I'm using Javascript because I don't have access to Visual Studio.
The idea is there's a Parent List item in a Document Library, called a Tender, and 4 associated lists with related entries. Some of them are lookups, some of them are just text fields that have a field that matches a key value in the Tender entry.
I have already written a function that seemed to delete all items matching the field, "Collective #" (display name) or "Collective_0020__0023_"
(internal name) from any specific list. My next imagined step was to call the method once for each list containing items to be deleted. This didn't work! I have a feeling it has to something to do with Asynchronous programming (something I don't understand) that prevents the method from deleting items from more than just the list from the first function call.
I had originally tried calling the method five times, in a for loop, like so:
for (var i in lists) {
deleteListItemsByCollectiveNum(lists[i], strCollectiveNum);
}
thinking it would delete from one, then the next, then the next, etc. This didn't work.
My next iteration of the process was to call the deleteListItemByCollectiveNum method recursively. Such as shown in the code below. This has exactly the same results.
Here's the updated code, as of June 27, 2013:
function deleteTenderAndAssociatedItems(strCollectiveNum) {
// List of all Lists/Libraries to be included in the clean up
// Tenders is last because it's the parent.
var lists = [
"Tenders - Add Docs",
"Tender Addenda",
"Tender Tasks",
"Tender Log",
"Tenders"
];
var confirmMessage = "Are you sure you want to remove this Tender and all associated data from SpecTrac?";
var position = 0;
//start by deleting from the first list
deleteListItemsByCollectiveNum(lists[position], strCollectiveNum);
// Deletes all list items from a chosen list with Collective #'s matching strCollectiveNum parameter.
// The intention is to execute this method for each list that holds entries related to a specific Tender.
//
// Params:
// strListTitle - The Display name of the list or library that has a field externally displayed as "Collective #"
// or Internally as "Collective_0020__0023_" (unicode)
// strCollectiveNum - The Collective Number of the items you wish to delete.
//
function deleteListItemsByCollectiveNum(strListTitle, strCollectiveNum) {
//setup
var context = null;
var listRef = null;
var itemQuery = null
var itemQueryString = "";
var itemCollection = null;
var startCount = 0;
var endCount = 0;
//Get the Site Context - This is what we query for references to site objects.
var context = new SP.ClientContext.get_current();
//Get reference to list passed as string parameter
listRef = context.get_web().get_lists().getByTitle(strListTitle);
//init the query object
itemQuery = new SP.CamlQuery();
//build the query string
//Equiv to SELECT * FROM list WHERE CollectiveNum = strCollectiveNum (parameter)
itemQueryString = '<View><Query><Where>';
itemQueryString +='<Eq><FieldRef Name=\'Collective_x0020__x0023_\'/><Value Type=\'Text\'>' + strCollectiveNum + '</Value></Eq>';
itemQueryString +='<Eq><FieldRef Name=\'Collective_x0020__x0023_\'/><Value Type=\'Lookup\'>' + strCollectiveNum + '</Value></Eq>';
itemQueryString +='</Where></Query></View>';
//set the query string
itemQuery.set_viewXml(itemQueryString);
//Get the a collection of items that match strCollectiveNum
//"Execute the query"
itemCollection = listRef.getItems(itemQuery);
//Load these items and grab them from the context object.
//parameters define which methods will be executed at Success (param 1) and Failure (param 2);
context.load(listRef);
context.load(itemCollection);
//The first time we get here it continues to the deleteObjects method just fine.
//However, during the first _recursive_ function call this code below does _nothing_
//Debugger hits the breakpoint, I press continue, and next nothing. No success, no fail.
//This is significant.
context.executeQueryAsync(deleteObjects, fail);
//delete all objects when the query returns successfully
function deleteObjects() {
//get the item count. Used in debugging.
startCount = listRef.get_itemCount();
var modifiedCount = 0;
//The enumerator object let's us cycle through all items from the returned query on the list above.
var resultsEnumerator = itemCollection.getEnumerator();
var items = [];
//Deleting an object alters the enumerator in a fashion that prevents us from using the while loop properly.
//When we find one of the objects push it into a regular javascript array to be deleted.
while (resultsEnumerator.moveNext()) {
var currentItem = resultsEnumerator.get_current();
items.push(currentItem);
}
//Delete all items found.
for(i = 0; i < items.length; i++) {
items[i].deleteObject();
modifiedCount++;
}
//What I've noticed is that if there have been no items deleted when the query is executed it simply falls through, no success call, no fail call.
//So if the list has gone unmodified go on to the next step/piece, in this case execute success()
if (modifiedCount > 0) {
//Update the list after doing these deletes.
listRef.update()
context.load(listRef);
context.executeQueryAsync(success, fail);
} else {
success();
}
}
//run upon query success
function success() {
//Get the end item count and show it next to the original item count to verify items were deleted.
endCount = listRef.get_itemCount();
alert("start " + startCount + " end " + endCount);
if (position < lists.length) {
position++;
//recursive call;
deleteListItemsByCollectiveNum(lists[position], strCollectiveNum);
}
}
//Query failed for some reason. Show the exception.
function fail(sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
}
}
I'm sure there's something I'm not understanding about Asynchronous method calls.
How is it I can utilize the method I've created to delete all items matching the field from all five lists?
I've also put my code into a tinypaste in case you prefer to read a version with nicer tabs/indenting: http://tny.cz/188aa886
Update June 27, 2013:
I've modified the code as per people's suggestions:
- I've fixed the spelling mistake (now "Item*s*" instead of "Item") in the recursive method call (why didn't this give an error before?)
- I've removed Function.createDelegate() as per a commenter's suggestion, not replacing with a closure but just a regular method call.
- I've made modifications to the deleteObjects() method to account for situations where a list isn't modified.
I've also tried altering the order of the items in the Lists array, thinking that perhaps a different list would behave differently (perhaps because of my query that tests for a regular text field OR a lookup field?).
The first context.executeQueryAsync(deleteObjects, fail)
executes just fine, I get an alert showing me an item has been deleted ("start 2 end 1") and the recursive call goes through, it starts all over again. However! At this point the second async query call does nothing. It just stops. No call to deleteObjects() OR fail()
This is something significant. Any other ideas what could be happening? I still feel like there's a piece of the puzzle in regards to asynchronous calls that hasn't been touched up on yet. But.. Of course I don't know :)
Here's a simplified chunk of code to illustrate my problem more clearly:
function test() {
lists = ["Tenders - Add Docs", "Tenders"];
var index = 0;
show_counts(lists[index]);
function show_counts(strListTitle) {
var context = new SP.ClientContext.get_current();
var listRef = context.get_web().get_lists().getByTitle(strListTitle);
var queryString = "<Query><OrderBy><FieldRef Name='ID' /></OrderBy></Query>";
var itemQuery = new SP.CamlQuery();
itemQuery.set_viewXml(queryString);
var itemCollection = listRef.getItems(itemQuery);
context.load(itemCollection);
context.executeQueryAsync(function() {
alert(strListTitle + " " + itemCollection.get_count());
while(index < lists.length-1) {
index++;
show_counts(lists[index]);
}
},
function () {
alert("failed");
});
}
}
The query is basic - It returns all items ordered by ID. I face the same problem with this test code as I do with my actual code. The query runs the first time, shows me an alert: "(Name of List) (ItemCount)", recursively calls show_count(next list in lists array) and the second executeQueryAsync call simply does nothing.
Can anyone explain this behavior? This is my first touch of asynchronous programming. I thought I understood the basics but it seems to me I'm missing something fundamental.