Search This Blog

Tuesday, February 8, 2011

How to Manipulate the People Picker Control Using JavaScript


1. Introduction: While Microsoft provides some clear guidance on how to manipulate SharePoint 2007 controls using JavaScript (see the article here) this guidance does not include the People Picker control. The purpose of this article is to provide some of that missing guidance.

2. The People Picker: The People Picker control is more complex than other SharePoint form controls; while most of the SharePoint form controls provide a simple means to capture direct user input, the People Picker control goes beyond this simple functionality to allow the user to access and browse SharePoint’s security and accounts listings. Because of this added capability the control’s is more difficult to manipulate with JavaScript but, as you will see, there is still a way to use our method successfully.

3. The getTagFromIdentifierAndTitle Method: To locate the SharePoint form fields Microsoft recommends using the “getTagFromIdentifierAndTitle” function. The Microsoft SharePoint Designer Team tells us that “This function finds the HTML element rendered by a given SharePoint FormField control.” The signature of the getTagFromIdentifierAndTitle is as shown below:

getTagFromIdentifierAndTitle(tagName, identifier, title)

The function takes three parameters, tagName, identifier, and title:

• The first parameter, “tagName”, is the name of the field’s tag as it is rendered in HTML

• The second parameter, “identifier”, is the SharePoint field type

• The third parameter, title, is the value of the field’s “title” attribute as it is rendered in HTML and also the field’s display name in SharePoint

So, for example, to find a field named Title, which is a SharePoint Input control of the type Text Field, you would use the JavaScript below:

var titleField = getTagFromIdentifierAndTitle("Input","TextField","Title")

Once the value of the titleField variable is returned from the getTagFromIdentifierAndTitle, you can manipulate the Title field in a variety of ways by setting its properties programmatically. For example, if we wanted to now set the Title field to the value “New Title” we could do it like this:

titleField.value = “New Title”

4. People Picker Differences: So far so good, now let’s try to follow the same logic to find the value of a People Picker control, which for our example will be called “MyPeoplePicker”. Our function call might appear as the one shown below:

var vPeople = getTagFromIdentifierAndTitle("Input","Lookup","MyPeoplePicker")

But when we try to use this function to return the tag values of the People Picker control we get an “object set to null reference” error, or in other words, the getTagFromIdentifierAndTitle function cannot find a control matching the criteria we set. Several vain attempts later we learn that setting the parameters for the People Picker control is not easy or obvious. This is it not working because, as I stated earlier, the People Picker control is more complex.

If we could look inside the People Picker control we would see some surprising things, the first of which is that the People Picker control is actually not a Lookup control at all but actually a Rich Text Area, the same as the multiple lines of text control. Next we would see that the People Picker control type is “UserField_downlevelTextBox” – now who would ever guess that one? Finally, the last surprise is that the title of the People Picker control is not the same as the display title; instead its title will always be “People Picker”.

Armed with this new knowledge we have a new signature for our getTagFromIdentifierAndTitle function, which is shown below:

getTagFromIdentifierAndTitle("Textarea","UserField_downlevelTextBox","People Picker");

Now when we run our code again and this time it returns a value, the “object set to null reference” error is gone. So we are good to go – right?

5. Still More Surprises: Not so fast! We have a way to return the People Picker object but there are still issues, let’s do an example to illustrate this. We want to write some code to return the People Picker object and then set the objects display style to none, or in other words, hide the People Picker control from the user. Here is the code:

var vPeople = getTagFromIdentifierAndTitle("Textarea","UserField_downlevelTextBox","People Picker");

vPeople.style.display = “none”;

While this code will work with the other SharePoint controls it doesn’t work with the People Picker control. Like all things about the People Picker control, it’s a more complex. To make this line of code work for the People Picker we have to modify it as shown below:

vPeople.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.style.display="none";

Now who was expecting that? Here’s what is going on, the People Picker control is actually 12 (yes twelve!) levels below its HTML tag and so we have to use multiple parentNode declarations to navigate from the tag to the People Picker control. So our corrected code would be as it appears below:

var vPeople = getTagFromIdentifierAndTitle("Textarea","UserField_downlevelTextBox","People Picker");

vPeople.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.style.display="none";

Now our code finds and returns the People Picker control as well as manipulates it to hide it from the user. Problem solved!

Here is the entire code block:

//This runs the function when the form first loads
_spBodyOnLoadFunctionNames.push("validatePeoplPicker");

function validatePeoplPicker()
{
var assignedToSubmitter = getSPPeoplePicker("MyPeoplePicker");

//This function finds and returns the proper SharePoint control's HTML
function getTagFromIdentifierAndTitle(tagName, identifier, title) {
var len = identifier.length;
var tags = document.getElementsByTagName(tagName);
for (var i = 0; i < tags.length; i++) {
var tempString = tags[i].id;
if (tags[i].title == title && (identifier == ""
tempString.indexOf(identifier) == tempString.length - len)) {
return tags[i];
}}
return null;
}

//This function finds the People Picker control's Parent element
function getParentElementByTagName(baseNode, tagName) {
var currNode;
if (baseNode !== null) {
currNode = baseNode.parentNode;
while ((currNode !== null) && (currNode.nodeName != tagName)) {
currNode = currNode.parentNode;
}
return currNode;
}
else {
return null;
}}

//This function finds the People Picker control's Cell element
function getPeoplePickerCell(columnName) {
var search = 'FieldName="' + columnName + '"';
var nodes = document.getElementsByTagName("TEXTAREA");
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].title == "People Picker") {
var outerCell = getParentElementByTagName(nodes[i], "SPAN").parentNode.parentNode;
if (outerCell.innerHTML.indexOf(search) > 0) {
return nodes[i].parentNode;
}}}
return null;
}

//This function returns the People Picker object
function getSPPeoplePicker(columnName)
{
var cell = getPeoplePickerCell(columnName);
if(cell !== null)
{
return cell.childNodes[0].innerHTML;
}
else
{
return null;
} } }

6. Conclusion: The People Picker control definitely present more challenges to the SharePoint Designer/Developer, but by understanding it’s greater complexity you can begin to add dynamic functionality to your SharePoint forms using the People Picker control.

Thanks to Kiran Kakanur’s for his great blog - http://kiran-kakanur.blogspot.com/2010/02/hide-people-picker-control-in.html







14 comments:

Haresh Purohit said...

Hello Tom,

I am doing some validation on the people picker values and I have multiple people picker fields on the same form.

How do I differentiate these various people picker fields to validate a specific one.

Thanks in Advance

Haresh

Tom Molskow said...

Hey Haresh,

I haven't posted a blog on that subject yet (although I am working on one) so the best advice I can give you is to review a great blog on the subject which was written by Tom Winter and is located here.

I hope thet helps!

Tom

Anonymous said...

Tom,

Like Haresh, I'm trying to validate a people picker field with multiple people picker fields on the same form.

I'm actually wanting to validate that the people picker field in question is not blank when another field (Choice) is set to a certain value.

I tried using Tom Winter's code, but couldn't get it to work.

Avidly awaiting your thoughts on the matter.

Thanks!

Mark

Tom Molskow said...

Hey Mark,

I believe Tom Winter's function getSPPeoplePicker(columnName, value) has an extra parameter that is not needed/used - the "value" parameter - try removing that and then LMK if your code works.

Thanks!

Tom

Tom Molskow said...

Hey Mark,

I found the code, here is what you need:


var assignedToInputManager = getSPPeoplePicker("My People Picker");


//This function finds and returns the proper SharePoint control's HTML
function getTagFromIdentifierAndTitle(tagName, identifier, title)
{
var len = identifier.length;
var tags = document.getElementsByTagName(tagName);
for (var i=0; i < tags.length; i++)
{
var tempString = tags[i].id;
if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len))
{
return tags[i];
}
}
return null;
}

//This function finds the People Picker control's Parent element
function getParentElementByTagName(baseNode, tagName)
{
var currNode;
if(baseNode !== null)
{
currNode = baseNode.parentNode;
while((currNode !== null) && (currNode.nodeName != tagName))
{
currNode = currNode.parentNode;
}
return currNode;
}
else
{
return null;
}
}

//This function finds the People Picker control's Cell element
function getPeoplePickerCell(columnName)
{
var search = 'FieldName="' + columnName + '"';
var nodes = document.getElementsByTagName("TEXTAREA");
for(var i=0; i < nodes.length; i++)
{
if(nodes[i].title == "People Picker")
{
var outerCell = getParentElementByTagName(nodes[i], "SPAN").parentNode.parentNode;
if(outerCell.innerHTML.indexOf(search) > 0)
{
return nodes[i].parentNode;
}
}
}
return null;
}

With this code you can actually get the People Picker by Field Name!

Thanks!

Tom

Jody said...

Tom,

Awesome article. I'm facing the same problem others are having regarding specifying more than one People Picker column.

I hate to say this, and please believe me when I say, I don't entirely understand how to comment out the above code that you provided. Everything looks good, but where do I find the "baseNode" ID for finding the People Picker's control element?

I don't understand how this is defined or how to comment.

I've done as much as I can think to make this work, but I'm sadly at a loss. Any help would be greatly appreciated.

Thank you,

Jody

Tom Molskow said...

Hey Jody,

It's actually in the function getPeoplePickerCell method, where you are setting var nodes =

document.getElementsByTagName("TEXTAREA");

Then in the for each loop -

for(var i=0; i < nodes.length; i++)

- if nodes equals "Peopl Picker" -

if(nodes[i].title == "People Picker")

- you pass it to the getParentElementByTagName method -

getParentElementByTagName(nodes[i], "SPAN").

Does that help make it a little clearer?

Thanks!

Tom

Tom Molskow said...

Hello,

I just realized I left off one of the methods for the code above, here is the entire code block:

//This runs the function when the form first loads
_spBodyOnLoadFunctionNames.push("validatePeoplPicker");

function validatePeoplPicker()
{

var assignedToSubmitter = getSPPeoplePicker("Submitter");

//This function finds and returns the proper SharePoint control's HTML

getTagFromIdentifierAndTitle(tagName, identifier, title) {
var len = identifier.length;
var tags = document.getElementsByTagName(tagName);
for (var i = 0; i < tags.length; i++) {
var tempString = tags[i].id;
if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len)) {
return tags[i];
}
}
return null;
}

//This function finds the People Picker control's Parent element

function getParentElementByTagName(baseNode, tagName) {
var currNode;
if (baseNode !== null) {
currNode = baseNode.parentNode;
while ((currNode !== null) && (currNode.nodeName != tagName)) {
currNode = currNode.parentNode;
}
return currNode;
}
else {
return null;
}
}

//This function finds the People Picker control's Cell element

function getPeoplePickerCell(columnName) {
var search = 'FieldName="' + columnName + '"';
var nodes = document.getElementsByTagName("TEXTAREA");
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].title == "People Picker") {
var outerCell = getParentElementByTagName(nodes[i], "SPAN").parentNode.parentNode;
if (outerCell.innerHTML.indexOf(search) > 0) {
return nodes[i].parentNode;
}
}
}
return null;
}

//This function returns the People Picker object

function getSPPeoplePicker(columnName)
{
var cell = getPeoplePickerCell(columnName);
if(cell !== null)
{
return cell.childNodes[0].innerHTML;
}
else
{
return null;
}
}
}

Thanks!

Tom

Anonymous said...

Has anyone every gotten this to work with more than one people picker field? Using MOSS, I can't seem to get it to work.

Tom Molskow said...

Hello,

Interesting... What have you tried so far?

Thanks!

Tom

BobMac said...

Hi Tom,

How can I use this to compare the values of 2 pickers to ensure they do not equal the same values?

daw said...

Hi Tom,

I have this control in my custom webpart and it is not displayed for users.. i get error msg as 'The control is not available because you do not have the correct permissions.' even for users with FullControl...

User's group is modified to be viewed by 'Everyone' (Group settings --> Who can view the membership of the group? )

am new to sharepoint and could not trace the issue.

Kindly assist!

daw

Tom Molskow said...

Hey BobMac,

Sorry for the slow reply, please try this and LMK if this works:

At the very start of the example you see this line - var assignedToSubmitter = getSPPeoplePicker("MyPeoplePicker");

Change that line to the following two lines...

var valuePicker1 = getSPPeoplePicker("MyPeoplePicker1");

var valuePicker2 = getSPPeoplePicker("MyPeoplePicker2");

Then compare valuePicker1 to valuePicker2 with additional code.

I hope that helps!

Tom

Tom Molskow said...

Hey Daw,

Can you make sure your users have this permission level - ‘Browse User Information’? If not then grant them that permission and that should solve the issue.

Thanks!

Tom