3

I have a custom list to which I am applying JSLink to NewForm.aspx.

The list where JSLink is applied is : XXX and in the JSLink I am querying data to YYY from the list YYY there are two fields returned, first is Title and Second is Person or group.

I want to get the login name of the Person field. So, to get the log in name I am ensuring user from web and loading in ClientContext. As a result I am not getting the exact output from my code.

My code is:

var loginName;
// variable to store the list Title from which we are fetching data of the responsible person
var listTitle = "YYY";
//  variable for ListItemCollection of Responsible.
var collListItem;
// two dimentsional array which stores [0] = item ID and [1] = Responsible person [2] = PersonID [3] = ReturnValue to submit to list.
var itemArray = [];
// the variable to store to return value when submit click.
var returnValue;

$(document).ready(function () {
    // execute function after SP.js loaded
    ExecuteOrDelayUntilScriptLoaded(getResponsibleArray, "sp.js");    
    // set change event of Area drop down list.
    $('[id^=xxx_]').change(function () {
        // if user has selected any Area
        if ($('[id^=xxx]').val() != "0") {
            // set the responsible person of the selected area.
            SetPersonName($('[id^=xxx]').val());
        }
        else {
            // set label value to blank
            $('#personName').text("");
            returnValue = "";
        }
    });
});

(function () {

    var OverrideCtx = {};
    OverrideCtx.Templates = {};
    OverrideCtx.Templates.Fields = {
        "Person": {
            "NewForm": renderResponsible
        }
    };
    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(OverrideCtx);

})();

function renderResponsible(ctx) {    
   return "<label id='personName'>hello</label>";
}
function getResponsibleArray() {

        clientContext = SP.ClientContext.get_current();
        web = clientContext.get_web();

        var oList = clientContext.get_web().get_lists().getByTitle(listTitle);
        var camlQuery = new SP.CamlQuery();
        camlQuery.set_viewXml("<View><Query><OrderBy> <FieldRef Name='ID' Ascending='TRUE' /></OrderBy></Query></View>");
        collListItem = oList.getItems(camlQuery);
        clientContext.load(collListItem);
        clientContext.executeQueryAsync(
            // Success function
            function (sender, args) {
                PopulateArray();
            },
        // Failure function
        function (sender, args) {
            alert('Error: ' + args.get_message() + '\n' + args.get_stackTrace());
        });    
}
function PopulateArray() {
    var listItemEnumerator = collListItem.getEnumerator();
    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
        var id = oListItem.get_item('ID');
        var person = oListItem.get_item('Person').get_lookupValue();
        var personId = oListItem.get_item('Person').get_lookupId();
        getUserLoginName(personId);
       itemArray.push([id, person, personId]);
    }
}
function SetPersonName(id) {
    for (var i = 0; i < itemArray.length; i++) {
        if (itemArray[i][0] == id) {
            $('#personName').text(itemArray[i][1]);
            returnValue = itemArray[i][2] + ';#' + itemArray[i][1];
            break;
        }
    }
}
function getUserLoginName(id)
{

    responsibleUser = web.getUserById(id);
    clientContext.load(responsibleUser);
    clientContext.executeQueryAsync(
        // success
        function (sender,args)  
        {
            PopulateVariable();
        },
        // failure
        function (sender,args)
        {       
            alert('Error: ' + args.get_message() + '\n' + args.get_stackTrace());
        }
    );
}
function PopulateVariable()
{
    alert(responsibleUser.get_loginName());
}

Here, what I am getting is if multiple persons are return from the list YYY then I am getting only the last person's log in name.

How to handle the calls?

  • Is there a reason you need the login name or would the e-mail work? You can easily retrieve the e-mail from your first call. – John-M Aug 12 '14 at 13:12
  • The reason behind to get the login name is to return the value of Person or Group field to SharePoint using JSLink. i.e. to submit data of Person or Group field we have to supply the string of login name like, [{\"Key\":\"i:0#.w|domain\\userName\"}] – users1100 Aug 12 '14 at 13:43
12

Basically what you are looking for is the Deferred loading mechanism (javascript 'promises'). The code block below returns a so-called 'promise'.

This allows you for chaining different methods (and is a bit more controllable than the previous answer given). I've applied this to all of my sharepoint apps to keep stuff organized as well (+ you can build some level of abstraction into your code).

Deferred method:

    function getAllSiteUsers() {
    var dfd = $.Deferred(function() {
        var userInfoList = hostWeb.get_siteUserInfoList();
        var users = userInfoList.getItems(SP.CamlQuery.createAllItemsQuery());
        this.appContext.load(users);
        this.appContext.executeQueryAsync(
            function() {
                // Successfull
                dfd.resolve(users);       // the argument here (users) is returned to the calling method below.
            },
            function(sender, args) {
                // Failure
                dfd.reject(args.get_message());
            });
    });
    return dfd.promise();
};

function loadSiteUsers() {
    var userPromise = getAllSiteUsers(); // calls method above
    userPromise.done(function(result) { // Done means: When the promise has been fullfilled
        var listItemEnumerator = result.getEnumerator();
        while (listItemEnumerator.moveNext()) {
            var oListItem = listItemEnumerator.get_current();
            if (oListItem.get_item("UserName")) {
                console.log(oListItem.get_item("FirstName") + " " + oListItem.get_item("LastName"));
            }
        }
    });
    userPromise.fail(function(result) { // fail means: promise was not kept - error or so
        // result is a string because that is what we passed to reject()!
        var error = result;
        console.log(error);
    });
}
9

In general you should avoid using executeQueryAsync inside of a loop -- for one you are generating an HTTP request with every call and second you just don't need to!

The general pattern for something like this is to first load all of the objects you want, then executeQueryAsync, then work with the returned data.

Here is an example that I've re-worked a little bit, but it should still generally work within what you've already defined.

function PopulateArray() {
    var SPClientContext = SP.ClientContext.get_current();
    var listItemEnumerator = collListItem.getEnumerator();
    var userArray = [];
    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
        var id = oListItem.get_item('ID');
        var person = oListItem.get_item('Person').get_lookupValue();
        var personId = oListItem.get_item('Person').get_lookupId();
        //setup responsibleUser objects here
        var responsibleUser = web.getUserById(personId);
        //load them so that the client context can fetch them all later
        SPClientContext.load(responsibleUser)
        //keep track of them in something like this array
        userArray.push(responsibleUser);
        itemArray.push([id, person, personId]);
    }
    //now that we're done with the loop - go fetch all the responsibleUsers we loaded
    SPClientContext.executeQueryAsync(function() {
        for (var i = 0; i < userArray.length; i++) {
             //do whatever you need to do with userNames here
             console.log(userArray[i].get_userName());
        }
        // do whatever else you need to do here
    }, function(sender, args) {
        //failure stuff
    });
}

The main advantage of using the JSOM over REST (specifically with executeQueryAsync) is that it supports batching items to retrieve instead of performing many individual network calls to complete the same operations.

When you tell the client context to 'load' an object you're saying 'retrieve that the next time you make a round trip.' You can tell it to load many items from varying webs, lists, etc. before running executeQueryAsync.

This provides much more efficient bandwidth use, and avoids asynchronous issues like what you're experiencing with multiple executions inside of a loop.

I should also add there may be a way to retrieve the user information you're looking for in the first call using an Include statement on the initial executeQueryAsync, but I couldn't find the relevent field name you could pull from the user information.

  • great comment @Jonh-M – egidiocs Feb 26 '16 at 16:17

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.