Question

Team,

I have 3 lists, (a) Issues (b) Risks (c) Incidents. I want to...

1 - Run a CAML query against (a) Issues list to get all the issues not closed. 2 - then run a loop through the titles, and use the title in a second CAML query to get all the (b) risks that match the current risk title in the first loop. 3 - add each matching risk into a string. 4 - after the loop is completed add the string to the issues list for the current issues list item in that cycle of the loop. 5 - do steps 2-4 with incidents i.e. while looping through the (a) issues list items, two different CAML queries will run, one for risks and another for incidents.

This page: http://www.sharepointnadeem.com/2014/10/sharepoint-using-deferredspromises-or.html

shows how to use a promise to sequence asynchronous CAML queries.

[note: the code doesn't do the string creation or list item updates yet, just trying to get the looping to work with the 3 lists first]

but this...

<script type="text/javascript">

$(document).ready(function() {  
    //don't exectute any jsom until sp.js file has loaded.
    SP.SOD.executeFunc('sp.js', 'SP.ClientContext', updateIssues);  
});


// display Issues
function updateIssues() {
    getIssueItemsWithCaml('SMOIFIssues').then(
            function (issueItems) {
                var issueListItemEnumerator = issueItems.getEnumerator();
                while (issueListItemEnumerator.moveNext()) {
                    var issueListItem = issueListItemEnumerator.get_current();

                    // log item details
                    console.log('ID: ' + issueListItem.get_item('ID') + ', Title:' + issueListItem.get_item('Title'));  

                    // update matching incidents
                    getIncidentItemsWithCaml('SMOIFIncidents',issueListItem.get_item('Title')).then(
                            function (incidentItems) {
                                var incidentListItemEnumerator = incidentItems.getEnumerator();
                                while (incidentListItemEnumerator.moveNext()) {
                                    var incidentListItem = incidentListItemEnumerator.get_current();

                                    // log item details
                                    console.log('Issue: ' + issueListItem.get_item('Title') + ', Incident: ' + incidentListItem.get_item('Title') + ', Type:' + incidentListItem.get_item('Account_x003a_AccountTypeDisplay').get_lookupValue());   


                                } console.log('Display of all incidents for ' + issueListItem.get_item('Title') + ' is done.');
                            },
                            function (sender, args) {
                                console.log('An error occured while displaying list items:' + args.get_message());
                            }
                        ); 

                    // retrive matching risks


                } console.log('Issue with status not completed is done.');
            },
            function (sender, args) {
                console.log('An error occured while displaying list items:' + args.get_message());
            }
        );   
} 

function getIncidentItemsWithCaml(listTitle2,issueItem2) {
    console.log('getIncidentItemsWithCaml: listTitle - ' + listTitle2 + ', issueItem - ' + issueItem2); 
    //use of $.Deferred in the executeQueryAsync delegate allows the consumer of this method to write 'syncronous like' code
    var deferred2 = $.Deferred();
    var clientContext2 = new SP.ClientContext.get_current();
    var list2 = clientContext2.get_web().get_lists().getByTitle(listTitle2);
    var camlQuery2 = new SP.CamlQuery();        
    camlQuery2.set_viewXml('<View><Query><Where><Eq><FieldRef Name="Issue" /><Value Type="LookupMulti">' + issueItem2 + '</Value></Eq></Where></Query></View>');
    var items2 = list2.getItems(camlQuery2);
    clientContext2.load(items2);
    clientContext2.executeQueryAsync(
        Function.createDelegate(this,
            function () { deferred2.resolve(items2); }),
        Function.createDelegate(this,
            function (sender, args) { deferred2.reject(sender, args); }));

    return deferred2.promise();
};  

function getIssueItemsWithCaml(listTitle) {
    //use of $.Deferred in the executeQueryAsync delegate allows the consumer of this method to write 'syncronous like' code
    var deferred1 = $.Deferred();
    var clientContext = new SP.ClientContext.get_current();
    var list = clientContext.get_web().get_lists().getByTitle(listTitle);
    var camlQuery = new SP.CamlQuery();        
    camlQuery.set_viewXml('<View><Query><Where><Neq><FieldRef Name="issueStatus" /><Value Type="Choice">Completed</Value></Neq></Where></Query></View>');
    var items = list.getItems(camlQuery);
    clientContext.load(items);
    clientContext.executeQueryAsync(
        Function.createDelegate(this,
            function () { deferred1.resolve(items); }),
        Function.createDelegate(this,
            function (sender, args) { deferred1.reject(sender, args); }));

    return deferred1.promise();
};  


</script>

produced this...

    ID: 1, Title: [issue title]
    getIncidentItemsWithCaml: listTitle - SMOIFIncidents, issueItem - [issue title]
    .
    .
    .
    ID: [last ID in issue list], Title: [last issue title]
    getIncidentItemsWithCaml: listTitle - SMOIFIncidents, issueItem - [last issue title]
    Issue with status not completed is done.

    Issue: [last issue title], Incident: [incident#1], Type:[incident type]
    Display of all incidents for [last issue title]
    Issue: [last issue title], Incident: [incident#2], Type:[incident type]
    Display of all incidents for [last issue title]

The console output shows it loops through all the issue list items, then outputs the incidents. And it doesn't filter the incidents based on the title.

again, my goal is;

  • Issue 1
    • matching incidents based on title of issue 1
    • matching risks based on title of issue 1
  • Issue 2
    • matching incidents based on title of issue 2
    • matching risks based on title of issue 2

etc

regards Michael

Was it helpful?

Solution

First, I'm guessing that the log of Display of all incidents for... is happening multiple times because it is on the same line as the closing bracket of the inner while loop. Try moving that down to the next line.

And what do you mean it doesn't filter by Issue title? In your last few log examples, Incident #1 and Incident #2 both have the same last issue title, so it looks to me like that did work.

But secondly, from what I can tell the code flow is happening as expected.

In the outer while loop, you log the ID and Title of the first Issue item. You then call getIncidentItems which executes all the way through immediately, so it logs that first console log statement. But what it returns is a Promise.

A Promise is asynchronous, meaning, your code will not wait for it to be resolved before moving on. So even though what's inside the .then() is waiting, what's outside is not. So as soon as you call getIncidentItems, the outer while loop continues, and you get another log entry for ID and Title of the next Issue long before the first Promise is resolved and the first .then() is executed.

If you want to see the log entries line up with what you have outlined that you are trying to see, you'll have to structure it more like this:

function updateIssues() {
    getIssueItemsWithCaml('SMOIFIssues').then(
        function (issueItems) {
            var issueListItemEnumerator = issueItems.getEnumerator();
            while (issueListItemEnumerator.moveNext()) {
                var issueListItem = issueListItemEnumerator.get_current();

                // first of all, let's save ourselves the
                // multiple calls to get_item('Title'),
                // it will make the code easier to read:

                var issueID = issueListItem.get_item('ID');
                var issueTitle = issueListItem.get_item('Title');

                // get matching incidents
                getIncidentItemsWithCaml('SMOIFIncidents', issueTitle).then(
                    function (incidentItems) {

                        // now that we have our incident items, we can start logging
                        console.log('ID: ' + issueID + ', Title:' + issueTitle);

                        var incidentListItemEnumerator = incidentItems.getEnumerator();
                        while (incidentListItemEnumerator.moveNext()) {
                            var incidentListItem = incidentListItemEnumerator.get_current();
                            var incidentTitle = incidentListItem.get_item('Title');
                            var incidentType = incidentListItem.get_item('Account_x003a_AccountTypeDisplay').get_lookupValue();

                            // log item details
                            console.log('Issue: ' + issueTitle + ', Incident: ' + incidentTitle + ', Type:' + incidentType);
                        }
                        console.log('Display of all incidents for ' + issueTitle + ' is done.');
                    },
                    function (sender, args) {
                        console.log('An error occured while displaying list items:' + args.get_message());
                    }
                );
            }
            // with this line here, you are actually going to see this logged
            // before you see the first Issue ID and Title log entry
            console.log('Issue with status not completed is done.');
        },
        function (sender, args) {
            console.log('An error occured while displaying list items:' + args.get_message());
        }
    );
}

Now, to fit the Risks into that, and not have even more nesting, you should look into Promise.all(). Promise.all() takes an array of Promises, and then waits for all of them to resolve before moving on to .then(), and .then() will receive an array of the results in the same order that you put in the promises. You can use that to send off both your query for matching Incidents and your query for matching Risks at the same time:

function updateIssues() {
    getIssueItemsWithCaml('SMOIFIssues').then(
        function (issueItems) {
            var issueListItemEnumerator = issueItems.getEnumerator();
            while (issueListItemEnumerator.moveNext()) {
                var issueListItem = issueListItemEnumerator.get_current();
                var issueID = issueListItem.get_item('ID');
                var issueTitle = issueListItem.get_item('Title');

                // here we get the two promises
                var incidentsRequest = getIncidentItemsWithCaml('SMOIFIncidents', issueTitle);
                var risksRequest = getRiskItemsWithCaml('RiskListName', issueTitle);

                // then we pass them to Promise.all to wait for them both to resolve
                Promise.all([incidentsRequest, risksRequest]).then(
                    function (allResponses) {

                        var incidentItems = allResponses[0];
                        var riskItems = allResponses[1];

                        // now that we have all the incident AND risk items, we can start logging
                        console.log('ID: ' + issueID + ', Title:' + issueTitle);

                        var incidentListItemEnumerator = incidentItems.getEnumerator();
                        while (incidentListItemEnumerator.moveNext()) {
                            var incidentListItem = incidentListItemEnumerator.get_current();
                            var incidentTitle = incidentListItem.get_item('Title');
                            var incidentType = incidentListItem.get_item('Account_x003a_AccountTypeDisplay').get_lookupValue();

                            // log item details
                            console.log('Issue: ' + issueTitle + ', Incident: ' + incidentTitle + ', Type:' + incidentType);
                        }
                        console.log('Display of all incidents for ' + issueTitle + ' is done.');

                        var riskEnumerator = riskItems.getEnumerator();
                        while (riskEnumerator.moveNext()) {
                            var riskItem = riskEnumerator.getCurrent();
                            var riskTitle = riskItem.get_item('Title');
                            console.log('Issue:', issueTitle, 'Risk:', riskTitle);
                        }
                        console.log('Display of all risks for ' + issueTitle + ' is done.');
                    },
                    function (sender, args) {
                        console.log('An error occured while displaying list items:' + args.get_message());
                    }
                );
            }
        },
        function (sender, args) {
            console.log('An error occured while displaying list items:' + args.get_message());
        }
    );
}

OTHER TIPS

Dylan,

Thanks for your help. I had a few issues with the solution because I was sending the wrong diagnostic information to the console.log which made it hard for myself to understand what was going on. But I worked it out.

For the boys and girls watching at home, here's the full code which includes taking the results of the incident and risk matches and going back and updating the issue list item "Incidents" and "Risks" column in the issues list.

Note: I realised that the only way to tag the incidents and risks with the right issue, was to pass the "issueID" into the promise as a third parameter. Once that was done, I was able to associate it properly with the incident and risk items. When I didn't the whole thing would only remember the last issueID in the loop. You can see what I mean by looking at the promise.all and the issueID var within the promise.all function (I also passed on the issueTitle to help make the console.log easier to read).

Code Below:

<script type="text/javascript">

// console.log ("start");

$(document).ready(function() {  
    //don't exectute any jsom until sp.js file has loaded.
    // console.log ("docready");
    SP.SOD.executeFunc('sp.js', 'SP.ClientContext', updateIssues);  
});

function updateIssues() {
    getIssueItemsWithCaml('SMOIFIssues').then(
        function (issueItems) {
            var issueListItemEnumerator = issueItems.getEnumerator();
            while (issueListItemEnumerator.moveNext()) {
            // console.log('main loop');
                var issueListItem = issueListItemEnumerator.get_current();
                var issueID = issueListItem.get_item('ID');
                var issueTitle = issueListItem.get_item('Title');
                //console.log('Updating ID: ' + issueID + 'Issue: ' + issueTitle);

                // here we get the two promises
                var incidentsRequest = getIncidentItemsWithCaml('SMOIFIncidents', issueTitle);
                var risksRequest = getRiskItemsWithCaml('SMOIFRisks', issueTitle);

                // then we pass them to Promise.all to wait for them both to resolve
                Promise.all([incidentsRequest, risksRequest, issueID, issueTitle]).then(
                    function (allResponses) {

                        var incidentUpdate = '';
                        var riskUpdate = '';
                        var incidentItems = allResponses[0];
                        var riskItems = allResponses[1];
                        var issueID = allResponses[2];
                        var issueItem = allResponses[3];

                        // now that we have all the incident AND risk items, we can start logging

                        // loop through incidents and match issue
                        var incidentEnumerator = incidentItems.getEnumerator();
                        while (incidentEnumerator.moveNext()) {
                            // console.log('incident loop');
                            var incidentItem = incidentEnumerator.get_current();
                            var incidentTitle = incidentItem.get_item('Title');
                            var incidentType = incidentItem.get_item('Account_x003a_AccountTypeDisplay').get_lookupValue();

                            if (incidentUpdate === "") {
                                var incidentUpdate = '\u2022' + incidentTitle + ' (' + incidentType + ')\n';                                    
                            } else {
                                var incidentUpdate = incidentUpdate + '\u2022' + incidentTitle + ' (' + incidentType + ')\n';   
                            }

                            // log item details
                            // console.log('issueItem: ' + issueItem + ', Incident: ' + incidentTitle + ', Type: ' + incidentType);
                            // console.log(incidentUpdate);

                        }
                        // console.log('Display of all incidents for ' + issueItem + ' is done.');

                        // loop through risks and match issue
                        var riskEnumerator = riskItems.getEnumerator();
                        while (riskEnumerator.moveNext()) {
                            // console.log('risk loop');
                            var riskItem = riskEnumerator.get_current();
                            var riskTitle = riskItem.get_item('Title');
                            var riskIssue = riskItem.get_item('Issue');

                            if (riskUpdate === "") {
                                var riskUpdate = riskUpdate + '\u2022' + riskTitle + '\n';                                  
                            } else {
                                var riskUpdate = riskUpdate + '\u2022' + riskTitle + '\n';  
                            }                               

                            // log item details                             
                            // console.log('issueItem: ' + issueItem + ', Risk:', riskTitle);
                            // console.log(riskUpdate);

                        }
                        // console.log('Display of all risks for ' + issueItem + ' is done.');

                        // update issue list item with incident and risk results
                        var clientContext = new SP.ClientContext.get_current();
                        var issueListUpdate = clientContext.get_web().get_lists().getByTitle('SMOIFIssues');
                        this.issueListUpdateItem = issueListUpdate.getItemById(issueID);
                        issueListUpdateItem.set_item('Incidents', incidentUpdate);
                        issueListUpdateItem.set_item('Risks', riskUpdate);
                        issueListUpdateItem.update();
                        clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));

                        },
                    function (sender, args) {
                        // console.log('An error occured while displaying list items:' + args.get_message());
                    }
                );
            }
        },
        function (sender, args) {
            // console.log('An error occured while displaying list items:' + args.get_message());
        }
    );
}

function onQuerySucceeded() {

    // console.log('Issue updated!');
}

function onQueryFailed(sender, args) {

    // console.log('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}


function getIssueItemsWithCaml(listTitle) {
    // console.log('get Issue   : ' + listTitle);
    //use of $.Deferred in the executeQueryAsync delegate allows the consumer of this method to write 'syncronous like' code
    var deferred = $.Deferred();
    var clientContext = new SP.ClientContext.get_current();
    var list = clientContext.get_web().get_lists().getByTitle(listTitle);
    var camlQuery = new SP.CamlQuery();        
    camlQuery.set_viewXml('<View><Query><Where><Neq><FieldRef Name="issueStatus" /><Value Type="Choice">Completed</Value></Neq></Where></Query></View>');
    var items = list.getItems(camlQuery);
    clientContext.load(items);
    clientContext.executeQueryAsync(
        Function.createDelegate(this,
            function () { deferred.resolve(items); }),
        Function.createDelegate(this,
            function (sender, args) { deferred.reject(sender, args); }));

    return deferred.promise();
};  


function getIncidentItemsWithCaml(listTitle,issueItem) {
    // console.log('get Incidents for: ' + issueItem);  
    //use of $.Deferred in the executeQueryAsync delegate allows the consumer of this method to write 'syncronous like' code
    var deferred = $.Deferred();
    var clientContext = new SP.ClientContext.get_current();
    var list = clientContext.get_web().get_lists().getByTitle(listTitle);
    var camlQuery = new SP.CamlQuery();        
    camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name="Issue" /><Value Type="LookupMulti">' + issueItem + '</Value></Eq></Where></Query></View>');
    var items = list.getItems(camlQuery);
    clientContext.load(items);
    clientContext.executeQueryAsync(
        Function.createDelegate(this,
            function () { deferred.resolve(items); }),
        Function.createDelegate(this,
            function (sender, args) { deferred.reject(sender, args); }));

    return deferred.promise();
};  

function getRiskItemsWithCaml(listTitle,issueItem) {
    // console.log('get Risks for    : ' + issueItem);  
    //use of $.Deferred in the executeQueryAsync delegate allows the consumer of this method to write 'syncronous like' code
    var deferred = $.Deferred();
    var clientContext = new SP.ClientContext.get_current();
    var list = clientContext.get_web().get_lists().getByTitle(listTitle);
    var camlQuery = new SP.CamlQuery();        
    camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name="Issue" /><Value Type="LookupMulti">' + issueItem + '</Value></Eq></Where></Query></View>');
    var items = list.getItems(camlQuery);
    clientContext.load(items);
    clientContext.executeQueryAsync(
        Function.createDelegate(this,
            function () { deferred.resolve(items); }),
        Function.createDelegate(this,
            function (sender, args) { deferred.reject(sender, args); }));

    return deferred.promise();
};      



</script>
Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top