Skip to main content
added code example suggestion
Source Link
Dylan Cristy
  • 12.8k
  • 3
  • 32
  • 72

Ok, to address your comments, and after looking again at your code posted above, here's an update with some further suggestions:

I know from the other code that you linked to that you retrieve a bunch of items that you want to copy, and then you loop through them all using jQuery's $.each() function, and in that loop you call your UpdateListItemUsingItemId function. (Which is actually very confusingly named, because in fact you are not using an item's ID to update it, you are creating a whole new list item.)

Looking at the code here:

  1. It looks like you are setting async: false on your $.ajax() call, presumably in order to try and control how the requests are going out since you are looping through a bunch of items using $.each(). This is not good. the first "A" of AJAX stands for asynchronous - it's meant to be asynchronous, so you should learn async programming techniques to handle that kind of code flow, instead of just bypassing it.
  2. It looks like you had to add the contentType: property directly to the AJAX options, because you typo'd the header. When you set the content-type header in the headers object, you should use a lower case t, so it should be "content-type": "app..." not "content-Type": "app...". We can correct that.

Luckily, a for...of loop will respect async / await, so we can reconstruct your loop using modern async programming techniques that simulate synchronous programming, which will make it easier to see where to catch errors and deal with them.

So I would get rid of your $.each() loop and this confusingly named function, and restructure things like this:

// get all the items you want to copy

for (var itemToCopy of allItems) {

    // build your new item payload
    var listItem = {
        // all your fields
        Field1: itemToCopy.Field1,
        Field2: itemToCopy.Field2,
        // etc
    }

    try {
        var newItemResponse = await $.ajax({
            url: _spPageContextInfo.webAbsoluteUrl + "/_api/Web/Lists/GetByTitle('ABCListNew')/items",
            method: 'POST',
            headers: {
                'accept': 'application/json;odata=verbose',
                'content-type': 'application/json;odata=verbose',
                'X-RequestDigest': $('#__REQUESTDIGEST').val()
            },
            data: JSON.stringify(listItem)
        });
        // if no error is thrown, to verify that the
        // new item was created, you _could_:
        //
        // console.log(newItemResponse.d);
        //
        // but that's not really necessary
    }
    catch (err) {
        var isFormDigestError = false;

        // i don't know exactly what you would have to do here,
        // but essentially you will need to figure out how to 
        // check the "err" object to see if it _is_ actually the form digest error

        if (isFormDigestError) {
            UpdateFormDigest(_spPageContextInfo.webServerRelativeUrl, _spFormDigestRefreshInterval);

            // don't forget to re try creating the item
            // that the process failed on
            try {
                var newItemRetry = await $.ajax({
                    url: _spPageContextInfo.webAbsoluteUrl + "/_api/Web/Lists/GetByTitle('ABCListNew')/items",
                    method: 'POST',
                    headers: {
                        'accept': 'application/json;odata=verbose',
                        'content-type': 'application/json;odata=verbose',
                        'X-RequestDigest': $('#__REQUESTDIGEST').val() // getting the _new_ form digest value
                    },
                    data: JSON.stringify(listItem) // but using the _same_ item you built above in this iteration through the loop
                });
            }
            catch (err2) {
                // i really don't know what to tell you if you get here, sorry
            }
        } else {
            // the error is not a form digest error.
            // what do you want to do in this case?
            // it's up to you.  cou could do nothing
            // and keep going through the loop, but i would probably
            throw err;
        }
    }
}

Ok, to address your comments, and after looking again at your code posted above, here's an update with some further suggestions:

I know from the other code that you linked to that you retrieve a bunch of items that you want to copy, and then you loop through them all using jQuery's $.each() function, and in that loop you call your UpdateListItemUsingItemId function. (Which is actually very confusingly named, because in fact you are not using an item's ID to update it, you are creating a whole new list item.)

Looking at the code here:

  1. It looks like you are setting async: false on your $.ajax() call, presumably in order to try and control how the requests are going out since you are looping through a bunch of items using $.each(). This is not good. the first "A" of AJAX stands for asynchronous - it's meant to be asynchronous, so you should learn async programming techniques to handle that kind of code flow, instead of just bypassing it.
  2. It looks like you had to add the contentType: property directly to the AJAX options, because you typo'd the header. When you set the content-type header in the headers object, you should use a lower case t, so it should be "content-type": "app..." not "content-Type": "app...". We can correct that.

Luckily, a for...of loop will respect async / await, so we can reconstruct your loop using modern async programming techniques that simulate synchronous programming, which will make it easier to see where to catch errors and deal with them.

So I would get rid of your $.each() loop and this confusingly named function, and restructure things like this:

// get all the items you want to copy

for (var itemToCopy of allItems) {

    // build your new item payload
    var listItem = {
        // all your fields
        Field1: itemToCopy.Field1,
        Field2: itemToCopy.Field2,
        // etc
    }

    try {
        var newItemResponse = await $.ajax({
            url: _spPageContextInfo.webAbsoluteUrl + "/_api/Web/Lists/GetByTitle('ABCListNew')/items",
            method: 'POST',
            headers: {
                'accept': 'application/json;odata=verbose',
                'content-type': 'application/json;odata=verbose',
                'X-RequestDigest': $('#__REQUESTDIGEST').val()
            },
            data: JSON.stringify(listItem)
        });
        // if no error is thrown, to verify that the
        // new item was created, you _could_:
        //
        // console.log(newItemResponse.d);
        //
        // but that's not really necessary
    }
    catch (err) {
        var isFormDigestError = false;

        // i don't know exactly what you would have to do here,
        // but essentially you will need to figure out how to 
        // check the "err" object to see if it _is_ actually the form digest error

        if (isFormDigestError) {
            UpdateFormDigest(_spPageContextInfo.webServerRelativeUrl, _spFormDigestRefreshInterval);

            // don't forget to re try creating the item
            // that the process failed on
            try {
                var newItemRetry = await $.ajax({
                    url: _spPageContextInfo.webAbsoluteUrl + "/_api/Web/Lists/GetByTitle('ABCListNew')/items",
                    method: 'POST',
                    headers: {
                        'accept': 'application/json;odata=verbose',
                        'content-type': 'application/json;odata=verbose',
                        'X-RequestDigest': $('#__REQUESTDIGEST').val() // getting the _new_ form digest value
                    },
                    data: JSON.stringify(listItem) // but using the _same_ item you built above in this iteration through the loop
                });
            }
            catch (err2) {
                // i really don't know what to tell you if you get here, sorry
            }
        } else {
            // the error is not a form digest error.
            // what do you want to do in this case?
            // it's up to you.  cou could do nothing
            // and keep going through the loop, but i would probably
            throw err;
        }
    }
}
Source Link
Dylan Cristy
  • 12.8k
  • 3
  • 32
  • 72

While you certainly can get a new form digest value by sending a POST to /_api/contextinfo as shown in the other answer you link to, there's another way to update the digest value.

Since you are clearly running this code on a page in SharePoint (because you are using _spPageContextInfo), you can update the form digest for that page by using a built-in SharePoint provided function:

UpdateFormDigest(_spPageContextInfo.webServerRelativeUrl, _spFormDigestRefreshInterval);

That should make subsequent use of $("#__REQUESTDIGEST").val() return a valid digest value.

You could add some code that catches any errors, and if it happens to be a form digest error, then call UpdateFormDigest, and then continue with your copying.