2

I have the below code which is looping through list names and trying to get the URL. However the code only returns the last list name url as it skips over the "currentcontext.executeQueryAsync(Function.createDelegate(this, result), Function.createDelegate(this, oncListQueryFailed));" the first and second time.

I'm getting the following JS Error:

Error: The property or field 'ServerRelativeUrl' has not been initialized. It has not been requested or the request has not been executed. It may need to be  explicitly requested.

Not sure why?

<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript">

var list;
var listRootFolder;

ExecuteOrDelayUntilScriptLoaded(init, "sp.js");

function init() {

    var Lists = ["List 1", "List 2", "List 3"];
    //load site
    Lists.forEach(function(listTitle) {

        console.log(listTitle);
        var currentcontext = new SP.ClientContext('/StrategicProjectOffice');
        list = currentcontext.get_web().get_lists().getByTitle(listTitle);
        listRootFolder = list.get_rootFolder();

        currentcontext.load(listRootFolder);
        currentcontext.executeQueryAsync(Function.createDelegate(this, result), Function.createDelegate(this, oncListQueryFailed));
    });
}

function result() {
    var listURL  = listRootFolder.get_serverRelativeUrl();
    console.log(listURL);

    var fullUrl = _spPageContextInfo.webAbsoluteUrl + "/SitePages/Project Page - " + listRootFolder.get_name() + ".aspx";
    console.log(fullUrl);
}

function oncListQueryFailed(sender, args) {
    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}

</script>
1
  • Im getting "Error: The property or field 'ServerRelativeUrl' has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested."
    – Jaron787
    Commented Mar 21, 2017 at 17:07

2 Answers 2

2

You can try this rest api call to fetch all List ServerRelativeURL with single call. whereas your code was making multiple calls to fetch server relative URL.

var listURLArray=[];
var listTitleArray=["Site Pages","Site Assets"];
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl +"/_api/Web?$expand=Lists/RootFolder&$select=Lists/RootFolder/ServerRelativeURL,Lists/Title",
type: "GET",
//async:false,        
headers: {
    "accept": "application/json;odata=verbose",
},
success: function (data) {
    $.each(data.d.Lists.results,function(ind,val){
    if(listTitleArray.indexOf(val.Title)>-1){
       listURLArray.push(val.RootFolder.ServerRelativeUrl);
    }
    })
},
error: function (error) {
    alert(JSON.stringify(error));
}
});

Also if you need the same to be synchronous then uncomment the part async:false

5
  • I would rather make multiple calls because im getting other data from the lists, not just the URL
    – Jaron787
    Commented Mar 21, 2017 at 12:08
  • what kind of data you need?You must have specified same in Question. Commented Mar 21, 2017 at 12:16
  • I would like to see it work on multiple calls so i can adapt my existing code which is too long to copy in just one question. I am effectively returning all lists items, then returning their underlying tasks and generating a report of columns for each task. As part of this i want to return the URL of the list so i can create a click through to the list page on one of the columns. I am happy to give you my entire code if need.
    – Jaron787
    Commented Mar 21, 2017 at 12:27
  • Im getting "Error: The property or field 'ServerRelativeUrl' has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested."
    – Jaron787
    Commented Mar 21, 2017 at 17:13
0

I think part of the problem is you are making multiple async calls and referencing the same variable (listRootFolder) and callback function in an outer scope.

Meaning - the reason the third one works is not that it is skipping over the executeQueryAsync the first and second time, it does call it, but then the third iteration of your forEach loop executes and steals ownership of the listRootFolder and success callback from the first two queries.

I see from your initial script tags that you are using jQuery. I would recommend using jQuery Deferreds to more explicitly manage the results of the async calls.

// set up a placeholder in the global scope to hold the results
var rootFoldersArray = [];

ExecuteOrDelayUntilScriptLoaded(init, "sp.js");

function init() {

    var Lists = ["List 1", "List 2", "List 3"];

    // collect the async requests
    var listRequests = Lists.map(function (listTitle) {

        var dfd = $.Deferred();

        // list and listRootFolder now exist within the scope
        // of the anonymous function executing on each list title,
        // so there is one "copy" of each variable for each list title
        var list;
        var listRootFolder;

        console.log(listTitle);
        var currentcontext = new SP.ClientContext('/StrategicProjectOffice');
        list = currentcontext.get_web().get_lists().getByTitle(listTitle);
        listRootFolder = list.get_rootFolder();

        currentcontext.load(listRootFolder);
        currentcontext.executeQueryAsync(Function.createDelegate(this, result), Function.createDelegate(this, oncListQueryFailed));

        // the callbacks are now also nested within the map anonymous function,
        // which should give them access to each of the relative "local" copies
        // of 'list' and 'listRootFolder'

        function result() {
            // on a successful result, store the result where we can access it later
            // and resolve the Deferred so we can keep track of which are done
            rootFoldersArray.push(listRootFolder);
            dfd.resolve();
        }

        function oncListQueryFailed(sender, args) {
            alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());

            // there is a way to tell your code that an
            // individual request failed by calling dfd.reject()
            // however that means you will have to set up error handling
            // further up the chain.  we're not getting into that complexity 
            // here so just resolve the Deferred as if it were success so that we know
            // that at least all of the queries have come back and we are not
            // waiting on anything
            dfd.resolve();
        }

        // return the promise
        return dfd.promise();
    });

    $.when.apply($, listRequests).done(function () {
        // this will not get executed until all three `executeQueryAsync` calls have completed
        logResults();
    });
}

function logResults() {
    // by the time this is called, 'rootFoldersArray' will have all the successful results
    rootFoldersArray.forEach(function (rootFolder) {

        var listURL = rootFolder.get_serverRelativeUrl();
        console.log(listURL);

        var fullUrl = _spPageContextInfo.webAbsoluteUrl + "/SitePages/Project Page - " + rootFolder.get_name() + ".aspx";
        console.log(fullUrl);

    });
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.