Insert batch of items in a list

Nov 17, 2011 at 3:58 PM

It's me again!  

I am able to add a new list item using "UpdateListItems" and specifying "ValuePairs"...thanks to the help that I got from here. 

Now, after my new project is created, I need to take 15 items from a reference list and add them as 15 items in another list, along with adding the ID that relates it back to the main project.  The 15 items are the milestones (tasks) that need to be accomplished to complete the project. 

Is there a novel way to handle this?  

FYI...The reference list of tasks has not changed in several years, and is not likely to.

Thanks,

Don

 



Coordinator
Nov 17, 2011 at 4:45 PM

Don:

You can do this by reading from the source list using GetListItems and writing into the destination list unsing UpdateListItems. It's really up to you how efficient you want to make it. For instance, you could simply call UpdateListItems inside the .each() in GetListItems as you process the XML. Since there are only 15 items, efficiency probably doesn't matter much.

M.

Nov 17, 2011 at 6:10 PM

Thanks Marc. 

Like this? :

$().SPServices({
   operation: "GetListItems",
   async: false,
   listName: "StandardMilestones",
   CAMLViewFields: "<ViewFields><FieldRef Name='Title' /></ViewFields>",
   completefunc: function (xData, Status) 
	{
	$(xData.responseXML).find("[nodeName='z:row']").each(function() 
		{
		var myTitle = $(this).attr("ows_Title");
		$().SPServices({
        	   	   operation: "UpdateListItems",
        	   	   async: false,
        	   	   listName: "ProjectMilestones",
		   batchCmd: "New",
        	   	   valuepairs: [["Milestone", myTitle], ["Project_x0020_ID", mynewID]],
        		   /*Do I need to have the "completefunc" statement here if I don't have a function to perform? */
    			});
		});
	}
	});
Do I need to include the "completefunc" line?  
Thanks,
Don
Nov 17, 2011 at 7:04 PM

That's correct Don, you don't need completfunc on the second SPService call if you aren't going to be doing anything after the update.  This is a prime example Marc of why I think opening up a generic function for batch updates would be useful...  Most of the code to do this is already in the library, it just needs to be opened up.

Coordinator
Nov 17, 2011 at 7:05 PM

Matt:

And that function might look like...?

M.

Nov 18, 2011 at 12:06 AM

I pulled this from SPUpdateMultipleListItems...

		var fieldNum;
		var batch = "<Batch OnError='Continue'>";
		for(i=0; i < itemsToUpdate.length; i++) {
			batch += "<Method ID='" + i + "' Cmd='" + opt.batchCmd + "'>";
			for (fieldNum=0; fieldNum < opt.valuepairs.length; fieldNum++) {
				batch += "<Field Name='" + opt.valuepairs[fieldNum][0] + "'>" + opt.valuepairs[fieldNum][1] + "</Field>";
			}
			batch += "<Field Name='ID'>" + itemsToUpdate[i] + "</Field>";
			if(documentsToUpdate[i].length > 0) {
				batch += "<Field Name='FileRef'>" + documentsToUpdate[i] + "</Field>";
			}
			batch += "</Method>";
		}
		batch += "</Batch>";

I pulled this out of the check on which op was passed:  switch(opt.operation)

 

				if(opt.updates.length > 0) {
					addToPayload(opt, ["updates"]);
				} else {
					SOAPEnvelope.payload += "<updates><Batch OnError='Continue'><Method ID='1' Cmd='" + opt.batchCmd + "'>";
					for (i=0; i < opt.valuepairs.length; i++) { 
						SOAPEnvelope.payload += "<Field Name='" + opt.valuepairs[i][0] + "'>" + escapeColumnValue(opt.valuepairs[i][1]) + "</Field>";
					}
					if(opt.batchCmd !== "New") {
						SOAPEnvelope.payload += "<Field Name='ID'>" + opt.ID + "</Field>";
					}
					SOAPEnvelope.payload += "</Method></Batch></updates>";
				}

As a matter of fact, I used this code as the basis for the roboCAML.BatchCMD function and here's what I got, but needs a little bit of work, i.e. Document support like you have for SPUpdateMultipleListItems, batch size that will control how many updates are made during one SPService call.  Would love to see something like this as a public function:

 

$.fn.SPServices.BatchCMD = function(opt) {
			var fieldNum, batch = "<Batch OnError='Continue'>", loopLength;

			switch ( opt.batchCMD.toUpperCase() ) {
				case "DELETE":
				loopLength = opt.IDs.length;
				while ( loopLength-- ) {
					batch += "<Method ID='" + ( loopLength + 1 ) + "' Cmd='" + opt.batchCMD + "'><Field Name='ID'>" + opt.IDs[loopLength] + "</Field></Method>";
				}
				batch += "</Batch>";
				return batch;

				case "NEW":
				//console.log(opt.valuePairs.length);
				loopLength = opt.valuePairs.length;

				for (i=0; i<loopLength; i++) {
					//console.log(opt.valuePairs[i].length);
					batch += "<Method ID='" + ( i + 1 ) + "' Cmd='" + opt.batchCMD + "'>";
					fieldNum = opt.valuePairs[i].length;

					for (fieldNames=0; fieldNames < fieldNum; fieldNames = fieldNames+2) {
						batch += "<Field Name='" + opt.valuePairs[i][fieldNames] + "'>" + opt.valuePairs[i][ ( fieldNames + 1 ) ] + "</Field>";
						//console.log(batch);
					}
					batch += "</Method>";
				}
				batch += "</Batch>";
				return batch;

				case "UPDATE":
				//console.log(opt.valuePairs.length);
				loopLength = opt.valuePairs.length;
				if ( opt.hasOwnProperty( "IDs" ) ) {
					for (i=0; i<loopLength; i++) {
						//console.log(opt.valuePairs[i].length);
						batch += "<Method ID='" + ( i + 1 ) + "' Cmd='" + opt.batchCMD + "'>";

						for ( fieldNames=0, fieldNum = opt.valuePairs[i].length; fieldNames < fieldNum; fieldNames = fieldNames + 2 ) {
							batch += "<Field Name='" + opt.valuePairs[i][fieldNames] + "'>" + opt.valuePairs[i][ ( fieldNames + 1 ) ] + "</Field>";
							//console.log(batch);
						}
						//Get ID from opt.IDs
						batch += "<Field Name='ID'>" + opt.IDs[i] + "</Field></Method>";
					}
				} else {
					for (i=0; i<loopLength; i++) {
						//console.log(opt.valuePairs[i].length);
						batch += "<Method ID='" + ( i + 1 ) + "' Cmd='" + opt.batchCMD + "'>";

						for ( fieldNames=1, fieldNum = opt.valuePairs[i].length; fieldNames < fieldNum; fieldNames = fieldNames + 2 ) {
							batch += "<Field Name='" + opt.valuePairs[i][fieldNames] + "'>" + opt.valuePairs[i][ ( fieldNames + 1 ) ] + "</Field>";
							//console.log(batch);
						}
						//Get ID from opt.IDs
						batch += "<Field Name='ID'>" + opt.valuePairs[i][0].ID + "</Field></Method>";
					}
				}
				batch += "</Batch>";
				return batch;

			default:
				batch = "INVALID CAML";
				return batch;
			}
		}; //End BatchCMD

 

Currently, I have to create this batch generator each time in code, which I feel the framework has already been built in SPServices...  Why not tweak what is in there and create a cool function to boot.  Here's some general usage function calls:

//Delete items
console.log( $().SPServices.BatchCMD({
		batchCMD: "Delete",	//Mandatory parameter
		IDs: [1,2,3]		//Mandatory parameter
	})
);

//roboCAML.BatchCMD for Update
//IDs and valuePairs must be in the same order or you'll kill KITTENS!!!! ~ used to be true... See the second call for a different way of identifying the correct ID
//If you have ideas to improve this, let me know!
//Update Items
console.log( $().SPServices.BatchCMD({
		batchCMD: "Update", //Mandatory Parameter
		IDs: [1,2,3],
		valuePairs: [["Rank", "Numero Uno", "Description", "Some Notes", "Col3", "Update3"], ["Col1", "1"], ["Col1", 1, "Col2", 2]] //Static Column Name, Value
	})
);
console.log( $().SPServices.BatchCMD({
		batchCMD: "Update",
		valuePairs: [[{ ID: "1" }, "Rank", "Numero Uno", "Description", "Some Notes", "Col3", "Update3"], [{ ID: 2 }, "Col1", "1"], [{ ID: 3 }, "Col1", 1, "Col2", 2]] //Static Column Name, Value
	})
);


//Create New items
console.log( $().SPServices.BatchCMD({
		batchCMD: "New",	//Mandatory parameter
		//Mandatory parameter
		valuePairs: [["JSPersonnelNameLookup", 1, "ModuleNotes", "ModuleNotes", "Description", "Googly Glop"], ["ListUID", 3]]  //Static Column Name, Value
	})
);

var titleColumnValue = 1;
console.log( $().SPServices.BatchCMD({
	//Mandatory parameter
	batchCMD: "New",
	//Mandatory parameter
	valuePairs: [	//Static Column Name, Value
							["ContentType", "Folder with Category", "BaseName", titleColumnValue, "Initiative_Name", titleColumnValue, "Document_x0020_Category", "BASE"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/Base Plan_FCR_IFS", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "Base Plan_FCR_IFS"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/CPS_Accelerator Report", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "CPS_Accelerator Report"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/GTRO_Technical Risk Assessment", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "GTRO_Technical Risk Assessment"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/Initiative Announcements_Post Launch Tracking", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "Initiative Announcements_Post Launch Tracking"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/Pre-TTR_TTR_Toolbox", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "Pre-TTR_TTR_Toolbox"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/Decision Sheets_Change Management_PACE", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "Decision Sheets_Change Management_PACE"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/Project Knowledge", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "Project Knowledge"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/SIMPL Working Documents", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "SIMPL Working Documents"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/Meeting Notes_Key Project Events", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "Meeting Notes_Key Project Events"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/Consumer_Technical Research", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "Consumer_Technical Research"],
							["ContentType", "Folder with Category", "BaseName", titleColumnValue + "/Marketing Concept_ZMOT_FMOT_SMOT", "Initiative_Name", titleColumnValue, "Document_x0020_Category", "Marketing Concept_ZMOT_FMOT_SMOT"]
						]
}) );

I know we briefly talked about this, but I'm not sure what you thought of it.  Show me some love bro!

Cheers,

Matt

 

 

Nov 18, 2011 at 2:50 PM
Edited Nov 18, 2011 at 2:53 PM
Show me some love bro!

+1

Nov 18, 2011 at 3:04 PM

Thanks for the help...I got it to work.  Just one note:   If you mis-spell the list name, it won't raise any kind of error. 

Don

Nov 25, 2011 at 1:48 PM

Sad this didn't even make the issue tracker...  At least there people could upvote and you'd see if there was interest in something like this.

Coordinator
Nov 25, 2011 at 1:50 PM
I'm just behind and haven't had a chance to look at your suggestions. :-)

M.
Coordinator
Nov 25, 2011 at 3:29 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Coordinator
Nov 25, 2011 at 9:28 PM
Matt:

I'm still not entirely sure what it is that you are looking for. Is it just that SPUpdateMultipleItems doesn't clearly support delete? In other words, can you explain in non-code terms what else you are looking for?

Thanks,
M.
Nov 26, 2011 at 2:09 PM

I didn't know that it didn't support delete (I thought it did), but SPUpdateMultipleItems doesn't have a working completfunc, so it'd be nice to have that.  Also over the last year or so, I have had to use this "string builder" in many pages.  

It's much easier to build up a batch update string of what I want, e.g. non-related items; instead of a dynamic CAML query string that would force me to call SPUpdateMultipleItems multiple times, which in my case, would have caused performance issues.

This will also promote DRY.  I'm looking at a 3500+ line script that was built previous to the function above and it's not very pretty...

 

Sorry for the code talk. :-P

Coordinator
Nov 26, 2011 at 8:02 PM
Matt:

I guess I'm still no exactly sure what you're looking for. Could you give me some business requirements examples?

M.
Nov 28, 2011 at 1:54 PM

This function was created b/c at the time I needed to update many items (600+) in different lists/libraries.  When looking in the library, I tried using UpdateMultipleItems, but I couldn't for two reasons.

  • completefunc
  • No control over batch size (Needed this so perf wasn't horrific)

I just checked to see if both of these bullets were still true.  As of this writing, this function has the same issues that prevented me from using it previously.  Using this code:

 

<script type="text/javascript" src="jquery-1.7.1.min.js"></script>
<script src="jquery.SPServices-0.7.0BETA1.js" type="text/javascript" language="javascript"></script>
<script type="text/javascript">
$().SPServices.SPUpdateMultipleListItems({
  listName: "Calendar",
  CAMLQuery: "<Query><Where><Eq><FieldRef Name='Title'/><Value Type='Text'>Title</Value></Eq></Where></Query>",
  valuepairs: [["Title", "Changed"]],
  completefunc: function(xData, Status) {
  	console.log(Status);
  	console.log(xData.responseText);
  }
}); 
</script>

I get undefined for Status and an error on xData.responseText b/c xData is undefined as well.

Looking at the source for UpdateMulti, I think both could be added, but it'll take some rework of the function.  Thanks for staying with me this long!

 

Cheers,

Coordinator
Nov 28, 2011 at 2:15 PM

I'm just wondering if your requirements are so specific to your needs if it doesn't make more sense for you to be building this custom for yourself. I haven't run across a situation where someone wnats to update a whole heap of items inn different lists at the same time. Generally, it's and update to one list, some processing or user actions, then an update to another list, etc.

M.

Nov 28, 2011 at 3:29 PM
sympmarc wrote:

I'm just wondering if your requirements are so specific to your needs if it doesn't make more sense for you to be building this custom for yourself. I haven't run across a situation where someone wnats to update a whole heap of items inn different lists at the same time. Generally, it's and update to one list, some processing or user actions, then an update to another list, etc.

M.

I'd love to have a function that would update a list, then update another built right into this library.  I can't do that with SPUpdateMulti b/c the completefunc doesn't work like the rest of the calls.  I've always got an undefined for the XML returned.  Do you get different results?