1

I am using the client side people picker, found here:

I have a list called "Jobs" with a Person or Group column called "Users". To update this column I have to pass in the user IDs of the users. However, the people picker doesn't return User IDs. It returns the username and "user key", but not the IDs.

I am able to get the user IDs using:

function getUserId(loginName) {
    var context = new SP.ClientContext.get_current();
    this.user = context.get_web().ensureUser(loginName);
    context.load(this.user);
    context.executeQueryAsync(
         Function.createDelegate(null, ensureUserSuccess),
         Function.createDelegate(null, onFail)
    );
}

What I am unsure of, or at least not the best practice approach, is how can I iterate through my people picker, get their user IDs and then call my function to update the list item using the retrieved IDs.

I know how to do each step, but the fact that getUserId() is getting the value asynchronously I don't know when to call my function to update the List Item.

Here is how I am proposing I do it:

Assuming I select 3 people in my picker and I click a button called "Submit".

  1. Get number of users in the picker: 3
  2. Loop: Through the picker users, calling getUserId()
  3. If success: Save user ID to an array. Call SaveItem() function
  4. SaveItem(): Check if array of user IDs are equal to the number of users in picker. If so, then safe to update the list item as we have all the IDs we want.
  5. If not, then go back to step 2.

Basically, it's a recursive approach that will fire off an async call that will invoke the SaveItem() method each time, but if it hasn't got all the userIDs it will re-fire the method with the next user in the people picker.

It just seems like an awful roundabout way of updating a Person/Group field when I have the client side people picker pulling back the user data for me... well all except for the user ID which is needed to update the field.

Am I doing this right?

  • You should implement jquery promise pattern. – Amal Hashim Mar 16 '16 at 15:55
  • How would this work with dynamic promises though? i.e. I don't know how many users are going to be in the picker. It could be 1 or it would be 20. If it's 20 then I need a way of creating 20 deferred objects dynamically. How do I know when they are complete? The examples I see are using something like $.when(promise1, promise2) but I don't know how many promises I will have, so I can't hardcode a $.when function to accept them as parameters. – Ray Hogan Mar 16 '16 at 16:13
  • You should loop the number of users and inside loop use promise to push results to an array. Also while pushing check if array objects equal total users. That condition will tell u if the iteration is finished or not. – Amal Hashim Mar 16 '16 at 16:32
  • Is it possible for the results to be returned at the exact same time? i.e. 3 users are selected. I loop through it three times, using promise to push results to the array. Before pushing the results I check the contents of the array. If I detect that two already exist, then I know this is the last one. So I push it and move to the final function. However, is it possible that 2 and 3 could fire at the exact same time, so they both detect that the array only has 1 element? So they both push into the array but the final check is never satisfied? – Ray Hogan Mar 16 '16 at 16:37
1

The way I would do it (and have done in the past) is to use an array to keep track of the asynchronous calls I'm making.

var mypromises = [];

When I call the asynchronous function, I add an identifier I can track to the array:

function getUserId(loginName) {
   var context = new SP.ClientContext.get_current();
   this.user = context.get_web().ensureUser(loginName);
   context.load(this.user);
   mypromises.push(loginName);
   context.executeQueryAsync(
      Function.createDelegate(null, ensureUserSuccess),
      Function.createDelegate(null, onFail)
   );
}

I pass the user object to my success and fail functions and remove the user from the array when the functions finish executing. Note that the code below will delete duplicates in the array if there are duplicate user names in the picker (there should never be, but should is such a strong word in the SharePoint universe)...

function ensureUserSuccess() {
    while ((ax = arr.indexOf(this.user.loginName)) !== -1) {
        arr.splice(ax, 1);
    }
    //... your code here...
}

function onFail(sender, args) {
    while ((ax = arr.indexOf(this.user.loginName)) !== -1) {
        arr.splice(ax, 1);
    }
    //... your code here...
}

I can then check the array's length at any time and if it's zero, I'm not in the middle of running any async queries. I don't even need to know how many queries I'm processing (but I can find out easily and compare that to the number of users selected in my picker, for example, to get an idea of progress).

CAVEAT: Remember, the array's length is also zero if you haven't started any queries yet.

  • "I can then check the array's length at any time and if it's zero, I'm not in the middle of running any async queries." -- Yes, but when do you check it? Do I create a timer function that checks the array size every x seconds? That doesn't sound like a good approach. I'd be better off with a recursive function. – Ray Hogan Mar 16 '16 at 17:05
  • @RayHogan Use two arrays: one for deferred object and another for user Ids. Use $.when.apply to pass in deferred array and the done function will only execute at the end where you can check the user id array – Aveenav Mar 16 '16 at 18:14
  • Nothing wrong with using a timer function if you're checking the progress of an asynchronous operation. That's what timers are for. – yogsodoth Mar 16 '16 at 18:24

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.