Saturday, February 1, 2014

SharePoint 2013 Client Side People Picker + SharePoint Hosted Apps + Knockout

It’s been quite a while playing with knockout, and trust me a great learning, every framework especially JavaScript has a stiff learning curve so as with knockout. Working on SharePoint Hosted App came across a valid use case of having a people picker to be part of application, integrating it using jQuery and as per Microsoft’s guideline it’s a cake walk (here). Well as I mentioned using with knockout was a bit of challenge, dealing with some really simple requirement can be scary sometime. So let’s look at it.

Requirement: User should be able to lookup user and also the control should be pre-populated initially if data exists.

Well I’m not going to go in details every bits of the code, I assume you already have some idea about how knockout works if not then a good starting point knockoutjs.com.

Okay enough talk, in a nut shell what you need is a custom binding handler to work with.

Before we get started, just a quick note, there is no one-size which fits all so the code which I’m going to show you will not suffice all the requirement, so my suggestion get inspiration and tweak as you need J

ko.bindingHandlers.kopeoplepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {

var schema = {};
schema['PrincipalAccountType'] = 'User';
schema['SearchPrincipalSource'] = 15;
schema['ShowUserPresence'] = true;
schema['ResolvePrincipalSource'] = 15;
schema['AllowEmailAddresses'] = true;
schema['AllowMultipleValues'] = false;
schema['MaximumEntitySuggestions'] = 50;
schema['Width'] = '280px';
schema["OnUserResolvedClientScript"] = function (elemId, userKeys) {
// get reference of People Picker Control
var pickerElement = SPClientPeoplePicker.SPClientPeoplePickerDict[elemId];
var observable = valueAccessor();
observable(pickerElement.GetControlValueAsJSObject()[0]);
console.log(JSON.stringify(pickerElement.GetControlValueAsJSObject()[0]));
};


// TODO: You can provide schema settings as options
var mergedOptions = allBindingsAccessor().options || schema;

// Initialize the Control, MS enforces to pass the Element ID hence we need to provide
// ID to our element, no other options
this.SPClientPeoplePicker_InitStandaloneControlWrapper(element.id, null, mergedOptions);

// Force to Ensure User
var userValue = ko.utils.unwrapObservable(valueAccessor());
var pickerControl = SPClientPeoplePicker.SPClientPeoplePickerDict[element.id + "_TopSpan"];
var editId = "#" + pickerControl.EditorElementId;
jQuery(editId).val(userValue);

// Resolve the User
pickerControl.AddUnresolvedUserFromEditor(true);


}
};



Microsoft’s Client Side People picker relies heavily on ID of element for the implementation of its controls (editor, hidden fields, etc.) so we have to work with it in our custom binding handler I wanted to highlight this point because most of the examples you see with KO is able to work with the element property itself)


#1 here we are initializing the People Picker Control.


 


#2 here we are looking for People Picker control using a special suffix which helps us in grabbing the control and then finding out the Editor element id (which actually has the value) and then making use of AddUnresolvedUserFromEditor(true) to resolve the user passed in a value. E.g. akhilesh.nirapure@zevenseas.com.


 


#3 here I’m providing my own implementation of what to do when we have a resolved user (from server) by using “OnUserResolvedClientScript” event. Again we here need to make use of People Picker control (we have to again find out by its id) and make use of GetControlValueAsJSObject() method which gets us the result as JSON.


 


Well the above implementation is not full blown code with exceptional handling it can be used as a reference point of how we can work with various event.


 


I’ve uploaded my sample code on GitHub (https://github.com/akhileshnirapure/sp13peoplepickerko) please feel free to fork it and extend it as you want.


 


Akhilesh Nirapure (@AkhileshN)