SharePoint 2013 Client People Picker

Mike BerrymanIn SharePoint 2013, a new people picker control was introduced called the Client People Picker.  It differs from the old People Picker control we’re familiar with from previous versions of SharePoint in some key ways.

Gone are the buttons to validate/search for a person, and instead the new Client People Picker utilizes an auto-complete drop down list of names to select from as you type.

Old version:

OldPeoplePicker

New version:

NewPeoplePicker

I had a client that wanted to switch all their old versions of the people picker in some custom solutions to the new version.  After some googling around, I found an article from Microsoft regarding using the Client People Picker in a SharePoint 2013 App, which did’t exactly apply to my situation but was a great starting point.

This article basically covered how to use Javascript to create and work with the Client People Picker.  Since my clients were semi-competent with HTML markup I didn’t want to actually create the control through Javascript so the client could make basic changes themselves, so by diving in to some of the _layouts pages that use it, I found a way to use markup to create the Client People Picker

<SharePoint:ClientPeoplePicker Required="false" ValidatorEnabled="true" ID="cppProjectManager" runat="server"
    InitialHelpText="<%$Resources:wss,aclinv_PickerIntialHelperText%>" Width="100%"
    PrincipalAccountType="User" Rows="1" AllowMultipleEntities="false" CssClass="ms-long ms-spellcheck-true" />

Your SharePoint page probably has the namespace needed for the control, but just in case…

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

I ran into my first gotcha at this point due to the design of the project.  I had my Client People Picker control in an angular-like form which uses a conditional to decide whether to show the form or not.  This form is not shown to the user until it can be determined that the user has proper permissions.  Apparently this causes the Client People Picker to not even render.  From what I could determine, SharePoint has some scripts that run on page load to actually setup any Client People Picker controls on the page.  Since the form (and thus, the Client People Picker) didn’t actually exist in the DOM until after the page loaded, these scripts didn’t find the control to setup.

I got around this by placing the Client People Picker markup in a hidden div on the page outside of the form, and then after the form loads using jquery to move the control from the hidden div to inside the form where it is supposed to be.

So now that the Client People Picker was functioning on the page I needed to utilize Javascript to interact with it.  Using Javascript with the old People Picker control basically meant having to examine the attributes on the control in the DOM to get any relevant information.  Not so anymore.  With the Client People Picker, SharePoint introduced a nifty new Javascript object to access any Client People Pickers on the current page.

This example shows how I was able to access the users/groups selected in the Client People Picker:

function _getPeoplePickerValues() {
    var promises = [];
    vm.request.ProjectManagerId = null;
    
    var allClientPeoplePickersOnPageObj = SPClientPeoplePicker.SPClientPeoplePickerDict;
    var allClientPeoplePickersOnPageObjKeys = _.keys(allClientPeoplePickersOnPageObj);
    var projectManagerCPPKey = null;
    for (var i = 0; i < allClientPeoplePickersOnPageObjKeys.length; i++) {
        if (allClientPeoplePickersOnPageObjKeys[i].indexOf("_cppProjectManager_") > -1) {
            projectManagerCPPKey = allClientPeoplePickersOnPageObjKeys[i];
            break;
        }
    }
    if (projectManagerCPPKey !== null) {
        var projectManagerCPP = allClientPeoplePickersOnPageObj[projectManagerCPPKey];
        var userKeys = projectManagerCPP.GetAllUserKeys().split(";");
        if (userKeys.length !== 1 || userKeys[0] === "") {
            //the user either didn't supply a value, or too many values
        }
        else {
            var userKey = userKeys[0]; //the full user login
            promises.push(usersRepo.getUserByLogin(userKey).then(function(user) {
                if (user !== null) {
                    vm.request.ProjectManagerId = user.Id;
                }
                else {
                    console.error(Error("Could not find user with login: " + userKey));
                }
            }, function(err) {
                console.error(err);
                throw err;
            }));
        }
    }
    return Promise.all(promises);
}

The SPClientPeoplePicker.SPClientPeoplePickerDict object is a Javascript object where each key corresponds to the ID of a Client People Picker on the page.  Since I had created my control using markup, I was able to set the ID to “cppProjectManager”, however when the control actually gets rendered into HTML the top-level container for the entire control ends up having an ID of something like “ctl00_PlaceHolderMain_cppProjectManager_TopSpan”.  This is key of the Client People Picker in the SPClientPeoplePickerDict object.  I used underscore to get an array of all the keys in the object and then search for the key that contains the “cppProjectManager” string, and from there I can access the control.  The GetAllUserKeys() function on the control returns a semi-colon separated string of usernames I could use to determine what users had been selected.

Setting a Client People Picker is very similar, as shown below:

function _setPeoplePickerValues() {
    function __setField(login, peoplePickerFieldId) {
        var allClientPeoplePickersOnPageObj = SPClientPeoplePicker.SPClientPeoplePickerDict;
        var allClientPeoplePickersOnPageObjKeys = _.keys(allClientPeoplePickersOnPageObj);
        var targetFieldCPPKey = null;
        for (var i = 0; i < allClientPeoplePickersOnPageObjKeys.length; i++) {
            if (allClientPeoplePickersOnPageObjKeys[i].indexOf("_" + peoplePickerFieldId + "_") > -1) {
                targetFieldCPPKey = allClientPeoplePickersOnPageObjKeys[i];
                break;
            }
        }
        if (targetFieldCPPKey !== null) {
            var targetFieldCPP = allClientPeoplePickersOnPageObj[targetFieldCPPKey];
            targetFieldCPP.AddUserKeys(login);
        }
    }
    
    var userFetchPromises = [];
    if (vm.request.ProjectManagerId !== null && vm.request.ProjectManagerId > 0) {
        userFetchPromises.push(usersRepo.getUserById(vm.request.ProjectManagerId).then(function(user) {
            __setField(user.Name, "cppProjectManager");
        }, function(err) {
            vm.message = "There was an error setting the Project Manager to user id " + vm.request.ProjectManagerId;
            console.error(err);
        }));
    }
    
    return Promise.all(userFetchPromises);
}

Basically I’m getting the control the same way as the first example, then passing a username to the AddUserKeys() function to set the Client People Picker.

SharePoint 2013 changed the way people pickers work, and in my opinion it was definitely for the better.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s