Mike is a Principal Consultant for ThreeWill. He has a passion for solving problems, most notably through products or platforms. He has almost 20 years of experience in product development and leadership, using the latest and greatest technologies, including open source technologies.
When building an app for SharePoint 2013, it’s likely that a people-picker will be needed at some point. Regardless of the chosen hosting model, there are challenges to using the standard People Picker control. This is particularly true in the case where the app’s user interface has been implemented as a “naked” HTML page (as opposed to the Microsoft-provided Default.aspx page). While I’ve discovered there’s an alternate people-picker available, In the case of my app I had a legitimate interest in building my own. In fact, to be a bit more precise, I had several goals:
- Build a people picker that is hosted in a “naked” HTML page
- Fully exploit the power of javascript and jQuery
- Be able to take advantage of any of the numerous open-source javascript libraries that are avaiable
After some research and experimentation I settled on the following:
- jQuery
- Select2 – a jQuery add-on that provides a replacement for select boxes – in particular it supports progressive search against a REST API
- Bootstrap – a collection of tools for quickly building a responsive, web-based UI
- SharePoint REST API
The HTML page I authored includes the following references:
<script type="text/javascript" src="../Scripts/libs/select2/select2.min.js"></script> <!-- Select2 CSS --> <link rel="Stylesheet" type="text/css" href="../Scripts/libs/select2/select2.css" /> <link rel="Stylesheet" type="text/css" href="../Scripts/libs/select2/select2-bootstrap.css" />
Next, I added input type=’text’ to page. Note that while Select2 works with select boxes in it’s most basic form, the control needs to be an input box of type text in order to exploit the built-in AJAX/REST search support.
<input type="text" id="peoplePickerDiv" class="form-control" />
Once my markup was in place I added the following function to bestow the “Select2” functionality on the input control:
function SetCustomPeoplePicker() { $("#peoplePickerDiv").select2({ placeholder: "Search for a person", minimumInputLength: 3, ajax: { url: $.QueryString["SPAppWebUrl"] + "/_api/web/siteusers", dataType: "json", data: function (term, page) { return { "$filter": "substringof('" + term + "', Title)" }; }, results: function (data, page) { return { results: data.d.results }; }, params: { contentType: "application/json;odata=verbose", headers: { "accept": "application/json;odata=verbose" } } }, id: function (person) { return { id: person.Id }; }, formatResult: function (person) { return person.Title; }, formatSelection: function (person) { return person.Title; }, formatNoMatches: function () { return "No people found. (Case Sensitive)"; }, escapeMarkup: function (m) { return m; }, dropdownCssClass: "bigdrop" }).on('change', function (e) { // Access to full data console.log($(this).select2('data')); }); }
A few things to point out in the above function:
- This code uses SharePoint’s RESTful endpoint for site users (/_api/web/siteusers) to get the set of site users for the app web.
- The data function supplies parameters to the AJAX/REST call; of particular importance is the use of the OData $filter parameter.
- The SharePoint-supplied substringof() function is to populate the $filter parameter. Inexplicably, this function treats data in a case-sensitive fashion!
- params and headers property must be supplied as shown above.
- The id property must be supplied as shown above, otherwise the returned results will not be selectable.
- Note that the change event handler demonstrates how to pull the selected value and its accompanying data.
- The RESTful endpoint for pulling site users does not search through user accounts that have not logged into SharePoint yet (unlike the SharePoint people picker, which shows all accounts).
Building your own people-picker lets you “have if your way” and provide a rich set of functionality for “picking people” in SharePoint.
12 Comments
JeffChilders
If I paste your code into a content editor, adjust the css and links to be correct, I get the following error in my console:
Unable to get property 'results' of undefined or null reference
Is there something more that must be done that is not included in your script?
Matthew Carter
Jeff are you using the default "="../Scripts/libs/select2/select2.min.js"" as that needs to have the file itself, so if you're not changing the ../ then you will have to specify it and or when you put the select2.min.js file (and the others) you'll need to have it pointing to the correct location, that's not out on the Internet (Cloud). I don't know if this helpful. I haven't looked at this more, just a quick thought! I don't code but I was trying to see how to look into this IF there is a need! Matt
Pete Skellly
Hello Jeff,
There should not be anything in addition the scripts/snippets listed.
As Matt indicated, please make sure you are referencing the scripts and CSS files from the correct locations. If you are using these in a SiteAssets library or other folder, ensure that the path is correct.
Pete Skelly
Johan
Excellent post! I was stuck with the same problem of trying to implement the peoplepicker in a naked HTML page and this example worked like a charm!
It also got me thinking. Instead of using /_api/web/siteusers to search only site users, why not use the search API (/_api/search) instead and set it to return people matches only? That will return a wider selection of user accounts - closer to the actual peoplepicker.
I got this working using a simple textbox and button to trigger the ajax call - here's my quick and dirty test code:
function doSearch()
{
var searchURL = "../_api/search/query?sourceid='B09A7990-05EA-4AF9-81EF-EDFAB16C4E31'&selectproperties='PreferredName'&querytext='" + $('#term').val() + "'"
$.ajax({
url: searchURL,
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success: function (search_data) {
var results = search_data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;
if (results.length == 0) {
$('#results').text('No matches found');
}
else {
var searchResultsHtml = '';
$.each(results, function (index, result) {
searchResultsHtml += "" + result.Cells.results[2].Value + "";
});
$('#results').html(searchResultsHtml);
}
},
error: function (search_data, errorCode, errorMessage) {
$('#results').append(errorMessage);
}
});
}
I would really like to get this working with Select2, but have not had any luck. Based of your example, I tried setting the ajax results parameter as follows:
results: function (data, page) {
var search_results = data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;
return { results: search_results };
},
This throws a TypeError: Cannot read property 'RelevanResults' of null.
I'm an amateur JavaScript & jquery coder and read the Select2 documentation, but am still stuck. Any advise?
Shahid
I am getting a "loading failed" when I enter any keywords in the input box, any clues?
SGSharePoint
I used above script and getting the results back. however, I get 2 same users back in the dropdown when I start typing the username.
Ridhvi p
Hello Mike, Does this support multiple people picker ?
jep
I can confirm that this does not accept multiple: true. Problem with returning data? Shoud be userid, displayname
Ľuboš Beran
My script doesn't make any ajax request, can you show me full implementation ? Field is changed, but when I write some text, no action is performed... :/
Alvin from Philippines
Hi Mike, I'm using angularjs together with select2 4.0.3 on sharepoint o365. This code doesn't work with the latest select2. Select2 devs, changed the format result function.
Navin
Where are the script file and css files located?