MS CRM 3.0 Multi-Select Boxes

Note: This was posted in 2006 when I was using MS CRM Software. I haven’t been near MS CRM in a couple years and I cannot provide help for the software.

CRM Multi-Select Box If you were not already aware, MS CRM only lets you choose one value from a select field. The workaround is normally to have one picklist field listing all possible values, and selecting one from that list adds it to the end of a disabled text field containing all the values. However this solution requires that you either have a special option in the list that clears the list, or you have to let users manually delete options, which gets messy. Knowing users,  you can’t leave it open to them to modify the text.

I’m still getting accustomed to the methods Microsoft uses in CRM, but I’ve improved on the solution many are using for multi-select fields. My solution builds on this idea with the picklist of values to add to the disabled text field. Note that disabled text fields aren’t normally submitted with crm, but whenever the list is updated it will set that field’s attribute ForceSubmit to true, causing it to save. It also has a picklist for removing values, which lists only the values that are in the text field. I haven’t seen this done before so hopefully this will catch on and make CRM that much more usable.

Edit (Aug 30): I was reminded today that this solution makes global edits impossible for multi-select fields. Adding a value to a multi-select field erases any existing values. The solution normally proposed to get around this is to make a lot of bit fields and have a tab full of checkboxes. I’ve read users prefer to see all options at once, but I don’t like the idea of making 300 more fields.

Edit (Nov 10): I have just updated this entry with the code I have been using without problems for over a month. It has some bug fixes and makes the disabled field bold when it has been changed and will be submitted.  It should be bug free, vista-ready, and IE7-friendly. For anyone upgrading from the last version, I’ve only changed the Global.js file.

Here’s how to implement it:

1) There is probably a better place for this, but I’ve added this to the end of my Global.js file:

/*************************************************
Multi-Select Functions V1.1 for Microsoft Dynamics CRM 3.0
Author: James Wilcox
Web: http://blog.jameswilcox.ca/tag/software/ms-crm-30/
Email: info@jameswilcox.ca
**************************************************/

/* This string separates the elements in the list.
It can behave strangely when working with existing
records which use a different separator. */
oSeparator = ';';

/********* Do not edit below here **********/
function removeAllOptions(selectbox){
var i;
/* Remove all but the first one as it has special
CRM properties that we need to submit the form. */
for(i=selectbox.options.length-1;i>=1;i--){
selectbox.remove(i);
}
}

function populateRemoveMulti(sourceid,oTarget){
// Fills a picklist with values from a given text field

if(crmForm.FormType == 5)
return;

allValues = getValues(sourceid);
removeAllOptions(oTarget);
if(allValues) {
// empty target first

// fill values
for(i=0; i 0) {
//separator = (crmForm.all[source].value.indexOf('; ') && crmForm.all[source].value.substring(-2) != '; ') ? '; ' : ';';

oMultiValues = crmForm.all[source].value.split(oSeparator);

oCleanValues = new Array();
for(i=0; i 1)
oCleanValues[oCleanValues.length] = oMultiValues[i];
}

// Return values sorted alphabeticalls
return oCleanValues.sort();
} else {
return false;
}
}
function setValues(target,oFinalValues) {
// Updates a field with new values

oCleanValues = new Array();
for(i=0; i 1)
oCleanValues[oCleanValues.length] = oFinalValues[i];
}

// Create Array
oJoinedValues = oCleanValues.join(oSeparator);

// Strip out erroneous separator at the beginning, if there
if(oJoinedValues.substring(0,oSeparator.length) == oSeparator)
oJoinedValues = oJoinedValues.substring(oSeparator.length);

// Update the text box
crmForm.all[target].DataValue = oJoinedValues;

}

////////// Array Manipulation ///////////
function addValue(oValues, newValue) {
// Go through oValues array looking for requested addition
foundValue = 0;
autoIndex = 0 ;
if(oValues.length && oValues.length > 0) {
for(i=0; i< oValues.length; i++) {
if(oValues[i] == newValue)
foundValue = 1;
}
autoIndex = oValues.length;
}

// If not already in the array, add it to the end
if(foundValue == 0)
oValues[autoIndex] = newValue;

// Then sort and return the array
return oValues.sort();
}

function delValue(oValues, remValue) {
// Go through oValues array looking for requested item
newValues = new Array();
if(oValues.length && oValues.length > 0) {
for(i=0; i< oValues.length; i++) {
if(oValues[i] != remValue){
// add items to keep
newValues[newValues.length] = oValues[i];
}
}
}

// Then sort and return the array
return newValues.sort();
}

////////// Request Management //////////
function addMulti(target,remover) {
// Get existing values in a sorted array
aValues = getValues(target);

// Get value to add
var oField = event.srcElement;
var aAddValue = oField.options[oField.selectedIndex].text;

// If there are already values there, make sure this isn't a duplicate
if(crmForm.all[target].value != '') {
aNewValues = addValue(aValues,aAddValue);
// Update field with new values
setValues(target, aNewValues);
} else {
crmForm.all[target].DataValue = aAddValue + '; ';
}

// Update Remove Item picklist
populateRemoveMulti(target,crmForm.all[remover]);

// Forces CRM to update this field
crmForm.all[target].ForceSubmit = true;

// Notify of change
crmForm.all[target].style.fontWeight = "bold";

// Reset selection
oField.selectedIndex=0;
}

function delMulti(target) {
// Get existing values in a sorted array
dValues = getValues(target);

// Get value to remove
var dField = event.srcElement;
var dDelValue = dField.options[dField.selectedIndex].text;
//alert(dDelValue);

// If there are still values there, remove the item
if(crmForm.all[target].value != '') {
dNewValues = delValue(dValues,dDelValue);
// Update field with new values
setValues(target, dNewValues);
}

// Update Remove Item picklist
populateRemoveMulti(target,dField);

// Forces CRM to update this field
crmForm.all[target].ForceSubmit = true;

// Notify of change
crmForm.all[target].style.fontWeight = "bold";

// Reset selection
dField.selectedIndex=0;
}

2) Make sure you have the 3 attributes you’ll need for this. I reccommend putting something like the letter z in front of the names for the last 2 fields so users aren’t trying to search with it.

  1. An ntext field which will contain the list of selections
  2. A picklist field containing everything the user can add to the 1st field. The first option should be something similar to ‘- Add -’ and this must be the first option as well as the default.
  3. A picklist field which will list values that can be removed from the first field. The only option should be something to the effect of ‘- Remove -’ and this should be the default.

3) Modify the form you want to work with:

  1. Add this to the form onload event:
    // fill remove category list with values currently in categories list.
    populateRemoveMulti('new_category_list',crmForm.all['new_category_remove']);

    The first field, ‘new_category_list’ is the schema name for the ntext field. The second option, ‘new_category_remove’ is the picklist that lets you remove values.

  2. Add the three fields you created: the ntext field should be disabled so users can’t cause chaos, and the picklists should probably be next to it.
  3. Add this to the add picklist’s onchange event:
    // Add selected value to list
    addMulti('new_category_list','new_category_remove');

    The first option is the ntext field to update and the second variable is the picklist that will show the values to remove.

  4. Add this to the remove picklist’s onchange event:
    // Remove selected value from list
    delMulti('new_category_list');

    The only variable here is the ntext field to update, the remove list will update it’s self.

Disclaimer: I have decided to post what I am using as I know how frustrating it can be telling users “No, CRM doesn’t do that”. Use of this code is at your own risk, and I cannot be held responsible for any negative effects. I have been using this code since August, 2006 with no problems aside from the lack of Global Edit functionality.

Tags:

15 Responses to “MS CRM 3.0 Multi-Select Boxes”

  1. Jerry Says:
    August 28th, 2006 at 2:00 pm

    Nice Job James..nicely implemented and well documented.

  2. Jim Says:
    November 8th, 2006 at 11:18 am

    Hello James,

    I am getting an error when running your code for the attributes. if I remove the single quotes from the schema name for the attribute in the code, I get an error message:
    Error: “zd_itemlist” is undefined.
    If I leave the quotes in, I get an error:
    Error: Object expected.
    Years ago I programmed in VB and played just a little with js, very little before moving to SQL Server programming.

    Any thoughts would be appreciated. I was estatic when I found your code but if we have to pay a consultant to code this functionality, I lose my chance to be a hero:)

    Thanks,

    Jim

  3. James Says:
    November 8th, 2006 at 2:37 pm

    I’ve been so busy lately I haven’t had a chance to post my latest revision. I had some bugs after this post and didn’t get around to updating my blog. I’ve been using the current revision for at least a month without errors so I’ll post an update by the end of this Friday.

  4. Ronald Lemmen Says:
    January 16th, 2007 at 6:51 am

    Hi James,

    Its great to see that a solution which I have created for crm 1.2 has been extended for 3.0! I’ll reference this post from my blog.

    Kind regards,

    Ronald

  5. Matt Says:
    January 17th, 2007 at 6:05 am

    Hi James,

    Great idea. Although I get the same issue described by Jim above. The OnLoad event will run, but will not work if I copy and paste the code from above. The single apostrophe’s taht surround the attribute are different. When I fix them to ‘ I get an error: Object expected (onload). Please let me know why this may be happening.

    Thanks

    Matt

  6. Justin Says:
    March 7th, 2007 at 3:54 am

    Hi James
    I seem to be getting the same problems and errors as Matt above. Any ideas?

  7. Ronald Lemmen Says:
    May 7th, 2007 at 12:51 am

    Hi All,

    I’ve written an article around many-to-many relationships in MS CRM 3.0 for the MS CRM team blog. One of the approaches uses this as base for the code. The errors that you see with this script have been solved in the download which comes along with the post. See here for more information:

    http://blogs.msdn.com/crm/archive/2007/02/15/many-to-many-relationships-in-ms-dynamics-crm-3-0.aspx

    Kind regards,

    Ronald Lemmen
    Microsoft CRM MVP

  8. Mark Says:
    August 30th, 2007 at 3:17 pm

    Rookie here. Do I have to add the picklist value in anywhere in the script?

  9. Mark Says:
    August 30th, 2007 at 3:21 pm

    Oh, by the way James , how long did it take you to build this?

  10. James Says:
    September 5th, 2007 at 8:42 pm

    As far as I remember, the picklist values were stored in the picklists schema, making it easier to maintain. I haven’t worked with MSCRM in 9 months, and no longer have access to it, so there are probably some security updates by now that break my code. Its too bad the code from Ronald’s post has been “phased out”. I think it took me a couple months working on this and many other fixes to get things running for my organization.

  11. G Says:
    September 14th, 2007 at 1:23 pm

    Your code is crap… this code works…

    oSeparator = ‘;’;

    function populateRemoveMulti(oList,oRemove)
    {
    // Fills a picklist with values from a given text field
    if(crmForm.FormType == 5) return;
    allValues = oList.value;

    removeAllOptions(oRemove);

    if(allValues)
    {
    // empty target first
    // fill values
    oMultiValues = oList.value.split(oSeparator);
    oMultiValues = oMultiValues.sort();

    for (i =0; i =1;i–)
    {
    selectbox.remove(i);
    }
    }

    function setValues(oTarget,oFinalValues)
    {
    // Updates a field with new values

    // Create Array
    oJoinedValues = oFinalValues.join(oSeparator);

    // Strip out erroneous separator at the beginning, if there
    if(oJoinedValues.substring(0,oSeparator.length) == oSeparator)
    oJoinedValues = oJoinedValues.substring(oSeparator.length);

    // Update the text box
    oTarget.value = oJoinedValues;
    }

    ////////// Array Manipulation ///////////
    function addValue(oValueList, newValue)
    {
    // Go through oValues array looking for requested addition
    foundValue = 0;
    autoIndex = 0 ;

    var oValues = oValueList.split(oSeparator);

    for(i=0; i 0)
    {
    for(i=0; i

  12. G -- Full Code Sample (working) Says:
    September 14th, 2007 at 1:40 pm

    oSeparator = ‘;’;
    function populateRemoveMulti(oList,oRemove){if(crmForm.FormType == 5) return;allValues = oList.value;removeAllOptions(oRemove); if(allValues){oMultiValues = oList.value.split(oSeparator); oMultiValues = oMultiValues.sort();
    for (i =0; i =1;i–){selectbox.remove(i);}}
    function setValues(oTarget,oFinalValues){oJoinedValues = oFinalValues.join(oSeparator);if(oJoinedValues.substring(0,oSeparator.length) == oSeparator)oJoinedValues = oJoinedValues.substring(oSeparator.length);oTarget.value = oJoinedValues;}
    function addValue(oValueList, newValue){foundValue = 0;autoIndex = 0;var oValues = oValueList.split(oSeparator);for(i=0; i 0){for(i=0; i

  13. Maximus Says:
    December 20th, 2007 at 2:01 am

    I would like to see a continuation of the topic

  14. Koopa Says:
    November 4th, 2008 at 9:58 am

    will this work with V4?

  15. James Says:
    November 4th, 2008 at 6:52 pm

    I highly doubt it will work in V4, it barely worked in V3. If they don’t have something like this in V4 I’d be highly disappointed in Microsoft… again.

Leave a Reply