1

I'm creating a status page in the site collection that needs to display List Status of all the subs below site collection. All the sub-sites have the same list 'Projects'. So the idea is to iterate all subwebs and report on the list in a tabular format. When I don't call function 'retrieveListItemsInclude()' then all the sub-sites are listed, when I do call the function an alert box appears but execution halts after 1st iteration. The function doesn't seem to return.

ExecuteOrDelayUntilScriptLoaded(RetrieveSites,"sp.js");

function RetrieveSites()
 {
    currentcontext = new SP.ClientContext.get_current();
    currentweb = currentcontext.get_web();
    //this.subsites = currentweb.get_webs();
    this.subsites = currentweb.getSubwebsForCurrentUser(null);
    currentcontext.load(this.subsites);
    currentcontext.executeQueryAsync(Function.createDelegate(this, this.SitesExecuteOnSuccess),Function.createDelegate(this, this.SitesExecuteOnFailure));
 }


function SitesExecuteOnSuccess(sender, args) 
 {
//var HTMLContent = '';
var enum1 = this.subsites.getEnumerator();
el = document.getElementById('LKMSubWebs');

var HTMLContent;

while (enum1.moveNext())
{
var Site = enum1.get_current();
//alert('hi123');
//Add Subsites to HTML Content
HTMLContent += '\n' + '<img title="css" alt="css" src="/_layouts/15/images/SharePointFoundation16.png" border="0">&nbsp;' + '<a class=subsitecls href='+ Site.get_serverRelativeUrl()+'>'+Site.get_title()+'</a> </br>';
//RootElement.append('<a class=subsitecls href='+ siteTitle+'>'+siteTitle+'</a> </br>');
   }
//set div to content
el.innerHTML = HTMLContent;
 retrieveListItemsInclude();

 }
 function SitesExecuteOnFailure(sender, args) 
 {
alert("error");
//alert(args.get_message());
 }




function retrieveListItemsInclude() {

    var clientContext = new SP.ClientContext.get_current();//(siteUrl);
    var oList = clientContext.get_web().get_lists().getByTitle('Project');

    var camlQuery = new SP.CamlQuery();
    this.collListItem = oList.getItems(camlQuery);

    clientContext.load(collListItem, 'Include(Id, DisplayName, HasUniqueRoleAssignments)');

    clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}

function onQuerySucceeded(sender, args) {

    var listItemInfo = '';
    var listItemEnumerator = collListItem.getEnumerator();

    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
        listItemInfo += '\nID: ' + oListItem.get_id() + 
            '\nDisplay name: ' + oListItem.get_displayName() + 
            '\nUnique role assignments: ' + oListItem.get_hasUniqueRoleAssignments();
    }

    alert(listItemInfo.toString());
}

function onQueryFailed(sender, args) {

    alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
0

You have the right general idea but the way you compose your calls can be improved a lot by grouping all of the loading into a single collection of objects before executing the async request to load the list item objects one time.

I started my answer below while working on another question you asked so I ditched a lot of your code unfortunately but it should give you somewhere to start and plug in your html generation logic. The following should log the "first" item title that is retrieved from every "Projects" list within the site collection to the console.

SP.SOD.executeOrDelayUntilScriptLoaded(function () {
    var myCtx = SP.ClientContext.get_current();
    var root = myCtx.get_site().get_rootWeb(); // may need to do this if in a sub site
    var webs = root.get_webs(); // fetch a reference to all the sub webs
    var query = new SP.CamlQuery();
    // only need to fetch the first item returned from each Project list
    query.set_viewXml('<View Scope="RecursiveAll"><Query></Query><RowLimit>1</RowLimit></View>');
    var queryItemCollections = [];
    // make sure to load the lists data so we can check
    // if the "Projects" list exists in each site
    myCtx.load(webs, "Include(Id, Lists)");
    myCtx.executeQueryAsync(function () {
        // push new SP.ListItemCollections into an array and tell the ctx to fetch them
        for (var i = 0; i < webs.get_data().length; i++) {
            var web = webs.get_data()[i], itms;
            if (checkForProjectsList(web)) {
                itms = webs.get_data()[i].get_lists()
                           .getByTitle("Projects").getItems(query);
                queryItemCollections.push(itms);
                myCtx.load(itms);
            }
        }
        myCtx.executeQueryAsync(function () {
            if (queryItemCollections.length == 0) {
                console.log("collection empty");
            }
            queryItemCollections.forEach(function (item) {
                // do work with individual list items here
                console.log(item.getItemAtIndex(0).get_item("Title"));
            });
        }, genericFailHandler);
    }, genericFailHandler);
    function checkForProjectsList(web) {
        var lists = web.get_lists().get_data();
        for (var j = 0; j < lists.length; j++) {
            var list = lists[j];
            if (list.get_title() == "Projects") {
                return true;
            }
        }
        return false;
    }
    function genericFailHandler(sender, args) {
        console.log(args.get_message());
    }
}, "SP.js");

If you need a different sort order you can modify the query object's viewXml

You can do more stuff to make this actually production ready, but this should demonstrate a basic approach. Something to note, this won't retrieve any projects from the root web if you also store more projects there, you would have to load that information and include it within the queryItemCollections if necessary.

  • This works- a few questions if you don't mind :) Sometimes the 'Project' list does not exist, so code errors, how can this be prevented? I also need to log 'Status' from the list column however sometimes this is empty, how can code continue when empty? In my code I started off with 'ExecuteOrDelayUntilScriptLoaded(RetrieveSites,"sp.js");' but you started the code with 'SP.SOD.executeOrDelayUntilScriptLoaded(function () {' whats the difference? – Orange Juice Jones Oct 29 '15 at 9:49
  • Sorry in the above 'Status' is a dropdown column that exists in Project list – Orange Juice Jones Oct 29 '15 at 10:40
  • This will already check if there is no "Project" list in a sub-web before trying to load it, if you have some site collections where there are no project lists add an if guard around myCtx.executeQueryAsync(...) so that it only executes if 1 or more Project lists was found... To get the Status column you should add a line right below where I have the console.log statement, you'll want to use item.getItemAtIndex(0).get_item("Status") -- I'll leave it to you to figure out how you handle it when that field doesn't have anything (I forget if it is null or equal to the empty string) – John-M Oct 29 '15 at 11:48
  • 'SP.SOD.executeOrDelayUntilScriptLoaded` is the same thing as ExecuteOrDelayUntilScriptLoaded - there are some members of SP.SOD that are not aliased to the window object like that though, so I just use the SP.SOD namespace so I don't have to remember which is which. – John-M Oct 29 '15 at 11:51
  • if the field name is StatusProject try changing myCtx.load(itms); to myCtx.load(itms, "Include(Title,StatusProject)"); -- you'll need to enumerate the name of all the fields you want to access there... and double check the field's real name is StatusProject in your list properties – John-M Oct 29 '15 at 12:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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