Setting multi-valued lookups in forms with jQuery

The solution presented here can be made to work in 2007, 2010 or 2013. I’ll explain later.

Setting SharePoint form fields using jQuery is a pretty standard practice nowadays, and not too hard either. We can fetch the current user, or a value in the query string, and use it to populate form fields, saving our users from having to do it themselves.

Multi-valued lookup columns, however, are not so easy to figure out, and understanding them requires some deep DOM inspection and trial and error.

Consider the form below. It could be an edit form or a new form, it doesn’t matter. They both work in the same way.

screenshot1

Here I have a multi-valued lookup column called “Project Document” with a number of documents in the parent library. The form shows two large text boxes (‘unselected values’ and ‘selected values’), along with an “add” and a “remove” button to shuttle the parent list items between the two text boxes.

Now you’d think that moving the choices from one text box to the other would be enough to “set” the value in the form, but you’d be wrong. Microsoft uses hidden elements to hold the “true” values present on the form, so in order to set that value we have to update both the visible and invisible elements in the form.

Digging into the DOM

The image below shows the DOM representation of the visible elements we are concerned about. This shows a 2013 environment, but the markup is the same in 2010, at least for this part of the form.

screenshot2

What we see here is the two text boxes (rendered as selects) along with options, about 20 or so in the “unselected” textbox, and one, titled “SET READ ONLY DATABASE” in the “selected” text box. Notice the title attributes on these selects, “Project Document possible values” and “Project Document selected values”. We will refer to these as we “move” an option from one select to the other using jQuery.

The second part, the part SharePoint uses under the hood to actually save the lookup values, is a hidden input element located just above the visible UI we have just seen.

screenshot3

There are actually three hidden input controls here, but we only care about the topmost one. It does not have a handy title attribute, but it has an ID of longstringofcrap_MultiLookup. This is the place where 2010 and 2013 are slightly different. The ID is a little different in 2010. We’ll fetch the input using jQuery by using the “ends with” selector, like so:

“[id$=’MultiLookup’]” for 2013 and “[id$=’MultiLookupPicker’]” for 2010.

The hidden input that stores the actual data for the selected items does so in a peculiar, pipe-delimited format. I’ll leave it to you to soak it in and try to make sense of it:

screenshot4

In our function we will need to create that crazy string using the option’s text and value along with that pipe-T thingy, and add or remove it from the input’s value attribute as needed.

Putting it all together

I wanted to write a function to add a selection by name, and another function to remove a selected option, also by name. I also wanted it to be as reusable as possible, so I made the column name and value as parameters to my functions. By including these functions in a globally-referenced JavaScript file, we can use this functionality everywhere on the site. Anyway here’s the code:

//var selector = "[id$='MultiLookupPicker']"; //for 2010 or 2007
var selector = "[id$='MultiLookup']"; //for 2013

function addChoice(text, columnName) {
    $("[title='" + columnName + " possible values'] option").each(function () {
        if ($(this).text() == text) {
            $(this).appendTo($("[title='" + columnName + " selected values']"));
            var multilookupPickerVal = $(selector).val();
            if ($(selector).val() == undefined || $(selector).val().length == 0) {
                $(selector).val($(this).val() + "|t" + $(this).text());
            }
            else {
                $(selector).val(multilookupPickerVal + "|t" + $(this).val() + "|t" + $(this).text());
            }
        }
    });
}

function removeChoice(text, columnName) {
    $("[title='" + columnName + " selected values'] option").each(function () {
        if ($(this).text() == text) {
            $(this).appendTo($("[title='" + columnName + " possible values']"));
            var multilookupPickerVal = $(selector).val();
            var valToRemove = $(this).val() + "|t" + $(this).text();
            var newValue = multilookupPickerVal.replace(valToRemove, "");

            $(selector).val(newValue);
        }
    });
}


A couple of things to point out. I added two variable declarations called “selector”. Make sure only one of them is not commented out – the appropriate one for you environment. For updating the visible text boxes I am using the jQuery appendTo function, a really neat function which removes a DOM element from its current home and places it into the specified location.

Edit: Thanks to jameel’s comment below I fixed an earlier issue where I was hard coding the column name in the function. He also verified the 2010 code works for SharePoint 2007 as well.

Declaratively provisioning a lookup column

In SharePoint development, developers are often stymied be seemingly simple tasks. Provisioning a lookup field using the declarative model in a solution package is one such task. The most common symptom of an improperly-provisioned lookup is that no error will occur on deployment, but the lookup field will have an empty reference to its parent list when viewed in List Settings. Developers will usually, and correctly, assume that the field is broken because the parent list did not exist yet when the field is provisioned. The most common way I have seen developers address this is to use a Feature Receiver to provision their lookups after declaratively provisioning everything else because they cannot figure out a way to make the declarative approach work.

Today on Stack Exchange I saw another question on this topic, and I felt compelled to write this post to clarify the issue.

The trick to doing this right is understanding that the order in which the package deploys the artifacts is important. Look at the image below, which shows a fairly representative simple data model for a SharePoint solution. The screen shot is from VS2102/SP2013, but it works exactly the same in 2010.

package

It shows fields, content types, and lists, deployed in that order. Now, the Projects list has a lookup to the Clients list. If I put that lookup in the SiteColumns element, it will fail, because it will have been provisioned in the wrong order. The practice I have developed to make this work is to put the Field element for the lookup directly under the ListInstance element it depends on. By doing this there is no possibility the lookup will be provisioned out of order, because SharePoint will deploy the stuff in an individual elements file in the order it appears in the file:

elements

Now, when I provision my related lists, everything is properly hooked up, and I didn’t have to write any code to do it.

…and by the way, I discovered this trick by taking a saved site template and opening it up using the “Import a Solution Package” project type in Visual Studio 2010 (you can learn a LOT about SharePoint’s provisioning process by doing this, by the way). And this approach works exactly the same in 2010 and 2013 (and, I would suspect, in 2007 as well).