Displaying a lookup as a dropdown in a CRM 2011 form

UPDATE (04JUN2013):  If you need to evolve this sample to support a one to many relationship represented as a multi-select, I like this multi-select control which can be themed with jQuery UI themes.

Another scenario from a customer:

We want to use a lookup to another entity because we want to store additional data in the  entities fields and option sets don’t provide this capability.  However, we want the selection of the entity in the form to be a dropdown to minimize the # of clicks a user must go through to select the entity.

HTML / JavaScript web resources and the OData service to the rescue!  First, this sample was loosely inspired by this post from my teammate Carlton Colter.  I took his idea, ran with it, applied it to this problem, and made my solution a reusable web resource that you can pass parameters to in order to apply it to any lookup on a form.  Here are the steps…

Add the lookup field to the form:

image

Notice that this lookup is required.  This solution respects the required metadata of the field and uses it to ensure the dropdown has a value before the form is saved.  Set the properties of the lookup making sure the Visible by default option is unchecked:

image

Insert an html web resource to the form with the following General settings configured (the code for the web resource is available further down):

image

The Custom Parameter(data) property is what makes this reusable.  You can figure out what needs to be passed in by looking at the JavaScript, but here is the comma separated format:

lookupEntitySetName,lookupEntityIdFieldName,lookupEntityDisplayFieldName,nameOfLookupFieldOnForm,webResourceName

Here is a longer description if the variable names aren’t clear enough:

lookupEntitySetName = Name of the entity set in the OData service for the entity.

lookupEntityIdFieldName = The id/primary key for the in the OData service.

lookupEntityDisplayFieldName = The field name from the entity that you want to display in the dropdown.

nameOfLookupFieldOnForm = The name of the actual lookup field that is hidden on the form.

webResourceName = The name of the web resource on the form.  This is set in the Add Web Resource dialog and always starts with WebResource_.

Make sure you set the properties on the Formatting tab like so:

image

Finally, there is a little bug that I gave up on.  So you will need to put a spacer next to or below the web resource:

image

This will prevent the dropdown for getting cut off if there is nothing to the right or bottom of it.  If it already has something to the right of it or below it, then this is unnecessary.  Bingo bango!  You now have a lookup that’s represented on the form as a dropdown complete with required validation:

image 

The dropdown is just UI trickery.  When you save the form, the value of the lookup is saved correctly.  In update scenarios, the form loads, reads the hidden lookup value, and sets the dropdown selected item appropriately.

Here’s the code for the html web resource:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>

    <head>

        <title></title>

        <script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>

        <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js" type="text/javascript"/>

        <script src="scripts/dropdownforlookup.htm.js" type="text/javascript"/>

 

        <!-- TOOD: move the needed styles into our own stylesheet. The CRM team doesn't support referencing their styles because they can change -->

        <link href="/_common/styles/fonts.css.aspx?lcid=1033" rel="stylesheet" type="text/css"/>  

        <link href="/_common/styles/global.css.aspx?lcid=1033" rel="stylesheet" type="text/css"/>  

        <link href="/_common/styles/select.css.aspx?lcid=1033" rel="stylesheet" type="text/css"/>  

    </head>

    <body style="background-color: rgb(246, 248, 250); border-width: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-left: 0px; padding-top: 0px;">

        <!-- TOOD: move the needed styles into our own stylesheet. The CRM team doesn't support referencing their style sheets because they can change -->

        <select id="lookupdropdown" class="ms-crm-SelectBox">

            <option value="Loading..." selected="selected">Loading...</option>

        </select>

    </body>

</html>

Here’s the code for the dropdown.htm.js file that it references:

$(document).ready(function () {

    var context = GetGlobalContext();

    var params = context.getQueryStringParameters();

    // The data passed to the web resource should be in the following format:

    //

    // lookupEntityName,lookupEntityIdFieldName,lookupEntityDisplayFieldName,nameOfLookupFieldOnForm,webResourceName

    //

    var dataParam = params.data.split(",");

    var lookupEntitySetName = dataParam[0];

    var lookupEntityIdFieldName = dataParam[1];

    var lookupEntityDisplayFieldName = dataParam[2];

    var nameOfLookupFieldOnForm = dataParam[3];

    var webResourceName = dataParam[4];

 

    var oDataQuery = context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc" +

    "/" + lookupEntitySetName + "?$select=" + lookupEntityIdFieldName + "," + lookupEntityDisplayFieldName;

 

    var lookupAttribute = window.parent.Xrm.Page.getAttribute(nameOfLookupFieldOnForm);

    var lookupArray = lookupAttribute.getValue();

 

    $.getJSON(oDataQuery, function (data) {

        var results = data.d.results;

        var lookupdropdown = $("#lookupdropdown");

 

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

            var option = "<option value='" + results[i][lookupEntityIdFieldName];

            option += "'>" + results[i][lookupEntityDisplayFieldName] + "</option>";

            lookupdropdown.append(option);

        }

 

        lookupdropdown.prepend("<option value='blank'></option>");

 

        if (lookupArray) {

            var lookupId = lookupArray[0].id.replace("{", "").replace("}", "").toLowerCase();

            lookupdropdown.val(lookupId);

        } else {

            lookupdropdown.val("blank");

        }

 

        $("#lookupdropdown option[value='Loading...']").remove();

 

        lookupdropdown.width("100%"); // couldn't figure out what was causing it to not stretch so I just forced it

 

        lookupdropdown.change(function () {

            var name = $("#lookupdropdown option:selected").text();

            if (name == "") {

                SetLookupValueToNull(nameOfLookupFieldOnForm);

            } else {

                var entityType = results[0].__metadata.type.replace("Microsoft.Crm.Sdk.Data.Services.", "");

                SetLookupValue(nameOfLookupFieldOnForm, lookupdropdown.val(),

                name, entityType);

            }

        });

    });

 

    var requiredLevel = lookupAttribute.getRequiredLevel();

 

    if (requiredLevel == "required") {

        // We want to drive whether our dropdown requires a value by the hidden lookup fields metadata

        // However, if we keep the lookup field's requirement level to required the form validation

        // will tell us it is required an force us to show it.

        lookupAttribute.setRequiredLevel("none");

 

        //NOTE: Modifying html that the CRM server sends down to the browser is not supported.

        // However, here we are finding a label for this control and updating its text.

        // This is relatively harmless and the likelihood of the CRM representing a label

        // for a control as anything other than an html label control is low. So I feel safe

        // enough to break the "don't touch the CRM html" rule. Even if it becomes an issue

        // at upgrade, we have one place where we can make the adjustments

        var label = $("label[for='" + webResourceName + "']", window.parent.document);

 

        // TODO: Improve this so it isn't tightly coupled to CRM generated html/css. Using undocumented css and server urls is unsupported.

        label.append("<IMG class=ms-crm-ImageStrip-frm_required alt=Required src='/_imgs/imagestrips/transparent_spacer.gif?ver=600790655'>");

 

        // TODO: add onsave to display an error message.

        window.parent.Xrm.Page.data.entity.addOnSave(function (context) {

            var name = $("#lookupdropdown option:selected").text();

            if (name == "") {

                var labelForWebResource = window.parent.Xrm.Page.getControl(webResourceName).getLabel();

                alert("You must provide a value for " + labelForWebResource + ".");

                context.getEventArgs().preventDefault();

            }

        });

    }

});

 

function SetLookupValue(fieldName, id, name, entityType) {

    if (fieldName != null) {

        var lookupValue = new Array();

        lookupValue[0] = new Object();

        lookupValue[0].id = id;

        lookupValue[0].name = name;

        lookupValue[0].entityType = entityType;

 

        window.parent.Xrm.Page.getAttribute(fieldName).setValue(lookupValue);

    }

}

 

function SetLookupValueToNull(fieldName) {

    if (fieldName != null) {

        window.parent.Xrm.Page.getAttribute(fieldName).setValue(null);

    }

}

@devkeydet

Leave a comment