Ramblings about MS Dynamics CRM 2011 and coding basics.

Monday, December 17, 2007

Non-Sequential Dynamic Picklist

I know I haven't posted to my blog in a long time because I have been busy with work.

Here is something that I finished up today.

I needed to create a set of dynamic picklists that could be non-sequential. The SDK gives a solution for a sequential based picklist, but that doesn't take in account for additional values in the future. I found this post by Greg Owens' post here and have edited and rearchitected parts of it. Since he helped me out, I have decided to post it to help you out!

Feel free to post comments. Thank you again Greg!

//****************************************************************
//**Non-Sequential Dynamic Picklist
//**------------------------
//**Developers: Greg Owens (Initial structure)
//** Mark Nagao (Rearchitecture)
//**Date: December 17, 2007
//**Purpose: Dynamic SubPicklist of non-sequential values
//**MSCRM 3.0: OnChange Event Code
//****************************************************************
//****************************************************************
//**Assign your main and sub picklist
var oPicklist= crmForm.all.new_mainpicklist;
var oSubPicklist = crmForm.all.new_subpicklist;

//**End of Assignment editing
//****************************************************************

//Saving and Resetting Original Picklist Values
if(!oSubPicklist.originalPicklistValues)
{
oSubPicklist.originalPicklistValues = oSubPicklist.Options;
}
else
{
oSubPicklist.Options = oSubPicklist.originalPicklistValues;
}

//****************************************************************
//**Add Arrays and specify values to display in SubPicklist

var oArray1 = new Array(0,1,3,4);
var oArray2 = new Array(0,2,5,6);


//**End of ARRAY Editing
//****************************************************************

//Check that Main Picklist value is not Null
if(oPicklist.DataValue != null)
{
switch(oPicklist.SelectedText)
{

//****************************************************************
//**Edit Case: Add a case for each Array

case "Array1":
filterPicklist(oArray1);
break;

case "Array2":
filterPicklist(oArray2);
break;


//**End of CASE editing
//****************************************************************

}
}

//Function to Filter SubPicklist
function filterPicklist(oDesiredOptions)
{

//Create temporary array to hold selected Values
var oTempArray = new Array();
//FOR loop to cycle through all SubPicklist Values
for (var i=oSubPicklist.length;i >= 0;i--)
{
//FOR loop to cycle through desired SubPicklist Values
for (var j=oDesiredOptions.length;j >= 0;j--)
{
//Compare i(SubPicklist Value) and j(Desired Value)
if (i == oDesiredOptions[j])
{
oTempArray[i] = true;
oDesiredOptions.splice(j,1);
}
}
}
//FOR loop to cycle through all SubPicklist Values
for (var i=oSubPicklist.length;i >= 0;i--)
{
//Compare with Temporay Array
if(oTempArray[i] != true)
{
//Remove Value from list if not TRUE in Temporary Array
oSubPicklist.remove(i)
}
}
}

//****************************************************************
//**Notes:
// - Place code in the OnChange of the Main Picklist
// - In the array, you must have two values, so if there is
// only one value, then include the 0 (null) value
//****************************************************************
//**End of Non-Sequential Dynamic Picklist Code
//****************************************************************

Labels: , , , , , , , ,

21 Comments:

Anonymous Anonymous said...

Hi Mark and thanks for acknowledging the mess that was my original work ;)

I ended up rewriting the awful example I'd originally used but am very glad to see that someone else has been able to take it a little further and find use :)

austincrm.blogspot.com has duly been added to my personal blogroll - looking forward to reading more of your posts.

Sunday, January 20, 2008 5:41:00 PM

 
Anonymous Anonymous said...

Hi Mark, thanks for script. I have been looking at your script and Greg's script and trying to understand the script. I have a question. I got the script working as is, but I needed to modify it. Instead of subpicklist relying on Mainpicklist field, could it be relying on lookup field? I tried changing the script, but it did not work.

I thought I would change the "case" portion of the script, so I changed case "1" to case "lookup item", but it did not work. Would this script work with a lookup field and if so, what do I need to change? Any help will be greatly appreciated.

Thanks.

Monday, January 21, 2008 10:40:00 PM

 
Blogger Mark N said...

Anonymous,

So what condition are you trying to have this multi-picklist sort off of?

For instance, if you are trying to have the picklist sort off of a Contact lookup, then you wouldn't be able to take in account all conditions, as the lookup would allow you to select new values.


The lookup would most likely have to either sort off of the Display value or the GUID of the lookup, depending on how you wanted to code it. Please respond with what you are exactly trying to do with the lookup and I can attempt to recreate it.

Thanks for reading this entry!

Wednesday, January 23, 2008 9:07:00 AM

 
Blogger Mark N said...

I edited the code below to filter off of a lookup. In the code below, you will see that I change then switch to look for a lookup's name (DataValue[0].name).

I set the "Main Picklist" to equal a lookup (at the beginning of the code), changed the SWITCH to look for a lookup name, and changed the CASE to that specific lookup's name.

//****************************************************************

//Check that Main Picklist value is not Null
if(oPicklist.DataValue != null)
{
//alert(oPicklist.DataValue[0].name);
switch(oPicklist.DataValue[0].name)
{

//****************************************************************
//**Edit Case: Add a case for each Array

case "Account's Name":
filterPicklist(oArray1);
break;

case "Array2":
filterPicklist(oArray2);
break;

//**End of CASE editing
//****************************************************************

I tested this out and it worked. Like I mentioned before, the potential problem with filtering off of a lookup is that the new records in the other entity will not have a specific spot in your logic. (I am assuming you are using it off of something like SUBJECT, etc). If you have other questions, let me know.

Thanks.

Wednesday, January 23, 2008 9:33:00 AM

 
Anonymous Anonymous said...

Hi Mark,

I tried the edited code you wrote and it worked great! Thanks for your help. The way I needed it to work was on Contact Entity, we needed a picklist value to change based on what Business Unit the user belongs to. I could see this being a problem with Contact, since new contact is consistently added. But hopefully, once I have the Business Units created, I should not have to create any more new Business Unit. Again, thanks for your help, I greatly appreciate it.

Friday, January 25, 2008 4:33:00 PM

 
Anonymous Anonymous said...

Hello,

This dynamic picklist script is a great script! Thanks! Works fine for me.

Now i would like to have two sub-picklists that depend on one main-picklist. Is that possible?

For example, let's say i have 3 picklists: Vendor, Product and License type. Is it possible when i select a specific vendor that both picklists Product and License type will be dynamicly filled?

Thanks for your answer!

Regards,
Pal

Wednesday, April 02, 2008 7:49:00 AM

 
Blogger Mark N said...

Funny that you should ask, as I made modifications to do just that yesterday. I will post a sample for you to reference. Note: This might not run, as I had to change the names quickly, but you can see and infer off of this sample. Please post if I need to clarify anything.


//****************************************************************
//**Non-Sequential Dynamic Picklist: Vendor TO Product and Vendor TO License
//**Developer: Mark Nagao
//**Date: April 1, 2008
//****************************************************************
//****************************************************************
//**Assign your main and sub picklist
var oPicklist= crmForm.all.new_vendor;
var oSubPicklist = crmForm.all.new_product;
var oSubPicklist2 = crmForm.all.new_license;

//**End of Assignment editing
//****************************************************************

//Saving and Resetting Original Picklist Values for 1
if(!oSubPicklist.originalPicklistValues)
{
oSubPicklist.originalPicklistValues = oSubPicklist.Options;
}
else
{
oSubPicklist.Options = oSubPicklist.originalPicklistValues;
}

//Saving and Resetting Original Picklist Values for 2
if(!oSubPicklist2.originalPicklistValues)
{
oSubPicklist2.originalPicklistValues = oSubPicklist2.Options;
}
else
{
oSubPicklist2.Options = oSubPicklist2.originalPicklistValues;
}

//****************************************************************
//**Add Arrays and specify values to display in SubPicklist Product
//Picklist Item 13 is “Other”

var oProdOption1 = new Array(0,2,4,6,8,10, 12);
var oProdOption2 = new Array(0,1,2,3,5,6,7,12);
var oProdOption3 = new Array(0,1,3,5,7,9,11);
var oother = new Array(0,1,2,3,4,5,6,7,8,9,10,11,12,13);
var onone = new Array(0,13);

//**Add Arrays and specify values to display in SubPicklist License
//Picklist Item 7 is “Other”
var oLicOption1 = new Array(0,1,2,3,7);
var oLicOption2= new Array(0,4,5,6,7);
var oLicOption3= new Array(0,2,4,7);
var oother2 = new Array(0,1,2,3,4,5,6,7);
var onone2 = new Array(0,7);

//**End of ARRAY Editing
//****************************************************************

//Check that Main Picklist value is not Null
if(oPicklist.DataValue != null)
{
switch(oPicklist.DataValue)
{

//****************************************************************
//**Edit Case: Add a case for each Array

case "1":
filterPicklist(oProdOption1);
filterPicklist2(oLicOption1);
break;

case "2":
filterPicklist(oProdOption2);
filterPicklist2(oLicOption2);
break;

case "3":
filterPicklist(oProdOption3);
filterPicklist2(oProdOption3);
break;

case "4":
filterPicklist(oother);
filterPicklist2(oother2);
break;


default:
filterPicklist(onone);
filterPicklist2(onone2);
break;

//**End of CASE editing
//****************************************************************

}
}
else
{
filterPicklist(onone);
filterPicklist2(onone2);
}

//Function to Filter SubPicklist1
function filterPicklist(oDesiredOptions)
{

//Create temporary array to hold selected Values
var oTempArray = new Array();
//FOR loop to cycle through all SubPicklist Values
for (var i=oSubPicklist.length;i >= 0;i--)
{
//FOR loop to cycle through desired SubPicklist Values
for (var j=oDesiredOptions.length;j >= 0;j--)
{
//Compare i(SubPicklist Value) and j(Desired Value)
if (i == oDesiredOptions[j])
{
oTempArray[i] = true;
oDesiredOptions.splice(j,1);
}
}
}
//FOR loop to cycle through all SubPicklist Values
for (var i=oSubPicklist.length;i >= 0;i--)
{
//Compare with Temporay Array
if(oTempArray[i] != true)
{
//Remove Value from list if not TRUE in Temporary Array
oSubPicklist.remove(i)
}
}
}


//Function to Filter SubPicklist2 (I changed the var to k and m so it wouldn’t be confusing
function filterPicklist2(oDesiredOptions2)
{

//Create temporary array to hold selected Values
var oTempArray2 = new Array();
//FOR loop to cycle through all SubPicklist Values
for (var k=oSubPicklist2.length;k >= 0;k--)
{
for (var m=oDesiredOptions2.length;m >= 0;m--)
{
if (k == oDesiredOptions2[m])
{
oTempArray2[k] = true;
oDesiredOptions2.splice(m,1);
}
}
}
for (var k=oSubPicklist2.length;k >= 0;k--)
{
if(oTempArray2[k] != true)
{
oSubPicklist2.remove(k)
}
}
}

//Remove value from subsequent picklists (so the parent resets the children)
oSubPicklist.DataValue = 0;
oSubPicklist2.DataValue = 0;

//****************************************************************
//**End of Non-Sequential Dynamic Picklist Code
//****************************************************************

Wednesday, April 02, 2008 9:41:00 AM

 
Anonymous Anonymous said...

Hello Mark N.,

Thanks a lot! Works great.

There is only one little difference with the original version i had. The difference is that now the original picklist values has to be in ascending order (the value with ID 1 at position 1 etcetera). Is there another way for this so i can sort the list?

Then i have another question, if you don't mind.
I have some data fields on my account entity that i want to display on a custom entity.

Is there a simple way to display data from the parent entity?
I know that i can map fields so fields are copied when creating a new child entity record but that's not what i want.

Thanks in advance.

Kind regards,
Pal.

Wednesday, April 02, 2008 1:41:00 PM

 
Blogger Mark N said...

Offhand I'm not sure about how to resort the picklist in an order other than numerical. I can do some thinking about this later on.

On your other question, are you trying to only view the data from another entity, or are you wanting to be able to update that field from either the original entity or the child entity? I am not sure how to do this, but it is something else that I can think about.

Thanks,
Mark N.

Wednesday, April 02, 2008 2:56:00 PM

 
Anonymous Anonymous said...

Thanks for your answer!

In addition to my last question: Viewing the data (1 or 2 fields) from a parent entity is enough. I do not need to be able to update the field, just viewing ..

Last night i did some more testing on the new (sub)picklist script and i discovered a strange thing:
When i view records in the assigned view i see both the main picklist and the subpicklist values. But when i open a record both the sub-picklists have no value.
At that moment i choose the correct value again and close&save the record. The assigned view display the record right, but when i open the sub-picklist values are empty again.

Am i doing something wrong?

Thanks in advance.

Thursday, April 03, 2008 2:20:00 AM

 
Anonymous Anonymous said...

Hello again,

When i remove the last two lines from the script, the last issue is solved. Is it a problem to remove those lines?

oSubPicklist.DataValue = 0;
oSubPicklist2.DataValue = 0;

Regards.

Friday, April 04, 2008 6:25:00 AM

 
Blogger Mark N said...

I have the last two lines in there so that you have to select the first picklist. My client had specified that they wanted it used in a particular order. It will be fine to remove those lines if you don't need them. Sorry I haven't got to those other questions, I am really busy and we have a Go-Live on Monday. I will try to get to it this weekend.

Thanks,
Mark

Friday, April 04, 2008 8:53:00 AM

 
Anonymous Anonymous said...

Maybe this is something for you instead of the removed lines:

// Disable sub-picklists if nothing selected in main picklist

if (crmForm.all.new_picklist.DataValue == null)
{
crmForm.all.new_subpicklist.Disabled = true;
crmForm.all.new_subpicklist2.Disabled = true;
}
else
{
crmForm.all.new_subpicklist.Disabled = false;
crmForm.all.new_subpicklist2.Disabled = false;
}

Good luck today!

Monday, April 07, 2008 1:22:00 AM

 
Anonymous Anonymous said...

Sorting works!

I compared the original code i had with the code you made and saw a difference:

//Function to Filter SubPicklist1
function filterPicklist(oDesiredOptions)
{

//Create temporary array to hold selected Values
var oTempArray = new Array();

//FOR loop to cycle through all SubPicklist Values
for (var i=oProduct.length-1;i >= 0;i--)

{
//FOR loop to cycle through desired SubPicklist Values
for (j=oDesiredOptions.length;j >= 0;j--)
{

//Compare i(SubPicklist Value) and j(Desired Value)
if (oProduct[i].value == oDesiredOptions[j])
{
oTempArray[i] = true;
oDesiredOptions.splice(j,1);
}
}
}
//FOR loop to cycle through all SubPicklist Values
for (var i=oProduct.length;i >= 0;i--)
{
//Compare with Temporay Array
if(oTempArray[i] != true)
{
//Remove Value from list if not TRUE in Temporary Array
oProduct.remove(i)
}
}
}


I changed some things (bold in de the code), now you can sort the picklist values within the attribute. The correct values appear sorted in the sub-picklists!

Monday, April 07, 2008 6:50:00 AM

 
Blogger Mark N said...

Thanks, I haven't had time to research any of those items, but hopefully I can get to them in the next week or so. Thanks for the update, I will take a look at those changes!

Cheers!
Mark

Monday, April 07, 2008 11:05:00 PM

 
Anonymous Anonymous said...

Hi Mark,

How did the 'go-live' go?
Did everything went okay?

Did you already think of my other question:

Is there a simple way to display data from the parent entity?
I know that i can map fields so fields are copied when creating a new child entity record but that's not what i want.

Viewing the data (1 or 2 fields) from a parent entity is enough. I do not need to be able to update the field, just viewing ..

Monday, April 14, 2008 6:53:00 AM

 
Blogger Mark N said...

The go-live went well, thanks for asking. :) It has been a crazy few weeks.

Ok, so, I thought about trying to view information from a child record and displaying the information. If I remember correctly, you may be able to create a workflow in CRM to do this.

The logic that I was thinking of would be:
- On create of child record, wait for -Parent record (related): new_field to CHANGE, then UPDATE the current record with new_field (from parent).

Then call itself again in order to be updated again. The issue with calling itself in a loop is that there will be an open workflow on every child record ALL of the time.

I haven't tested this out, so I could be mistaken. If I have time later, I will attempt to execute this and post back my findings.

Thanks,
Mark

Monday, April 14, 2008 8:47:00 AM

 
Blogger Mark N said...

Oh, and as to the code:
oSubPicklist.DataValue = 0;
oSubPicklist2.DataValue = 0;

I did that so that once you select the parent, it will force the user to select a subpicklist, and thus force a change of the subsequent list, etc. I ran into the same issue that you did, so I created some OnLoad JS to manually walk through the picklist action. I can post it if you want.

Monday, April 14, 2008 8:50:00 AM

 
Blogger Oz said...

Hi Mark,

Your solution to dynamic picklists was marvellous, solved a lot of my issues. I have a question for you. I have a parent entity, child entity 1 and child entity 2. Child entity 2 depends upon child entity 1 and 1 depends upon parent entity. Once I select the parent entity, child 1 should get automatically populated and so child 2. Instead of working on dropdowns can we have all the entites as lookups because my parent entity is a master which will be updated always and child 1 and 2 are also masters which are linked with each other. Does that ring a bell?

Sunday, December 21, 2008 2:57:00 PM

 
Blogger Patrick said...

How do I put two picklists within the same entity? I get one to work fine, but two do not.

The OnChange scripts for each of the two primary picklist attributes are crmForm.FilterPicklist();

Here's the OnLoad

// Used to help search the picklist items
Array.prototype.Contains = function(o)
{
var iLength = this.length;

for (var i = 0; i < iLength; i++)
{
if (o == this[i])
{
return true;
}
}

return false;
};

// This is the main function that will filter the picklists
crmForm.FilterPicklist = function x()
{
var oPrimaryPicklist = crmForm.all.new_crimperbrand;
var oRealatedPicklist = crmForm.all.new_crimpermodel;

if (oPrimaryPicklist.DataValue == null)
{
oRealatedPicklist.DataValue = null;
oRealatedPicklist.ForceSubmit = true;
oRealatedPicklist.Disabled = true;


return;
}


var oTempArray = new Array();

var iLength = oRealatedPicklist.originalPicklistOptions.length;

var aCurrentType = new Array();


switch (oPrimaryPicklist.DataValue)
{
case "1":
aCurrentType = new Array(1,2,3,4,5,6,7,8,9,10);
break;
case "2":
aCurrentType = new Array(11,12,13,14,15,16,17,18,19,20,21,22,23,24);
break;
case "3":
aCurrentType = new Array(25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41);
break;
case "4":
aCurrentType = new Array(42,43,44);
break;
case "5":
aCurrentType = new Array(45,46,47,48);
break;
case "6":
aCurrentType = new Array(49,50,51,52,53,54,55);
break;
case "7":
aCurrentType = new Array(56,57,58,59,60,61,62,63,64);
break;
case "8":
aCurrentType = new Array(65,66,67,68,69);
break;
}


for (var i = 0; i < iLength; i++)
{
if (aCurrentType.Contains(oRealatedPicklist.originalPicklistOptions[i].DataValue))
{
oTempArray.push(oRealatedPicklist.originalPicklistOptions[i]);
}
}

oRealatedPicklist.Options = oTempArray;

if (oTempArray.length > 0)
{
oRealatedPicklist.Disabled = false;
}
else
{
oRealatedPicklist.DataValue = null;
oRealatedPicklist.ForceSubmit = true;
oRealatedPicklist.Disabled = true;
}
}

var CRM_FORM_TYPE_CREATE = 1;
var CRM_FORM_TYPE_UPDATE = 2;

switch (crmForm.FormType)
{
case CRM_FORM_TYPE_CREATE://CREATE_FORM - try these and blow away the vars above
case CRM_FORM_TYPE_UPDATE: //UPDATE_FORM

var oRealatedPicklist = crmForm.all.new_crimpermodel;

oRealatedPicklist.originalPicklistOptions = oRealatedPicklist.Options;

if (crmForm.all.new_crimperbrand.DataValue == null)
{
oRealatedPicklist.Disabled = true;
}
else
{
var iPicklistValue = oRealatedPicklist.DataValue;
crmForm.FilterPicklist();
oRealatedPicklist.DataValue = iPicklistValue;
}

break;
}




// Used to help search the picklist items
Array.prototype.Contains = function(o)
{
var iLength = this.length;

for (var i = 0; i < iLength; i++)
{
if (o == this[i])
{
return true;
}
}

return false;
};

// This is the main function that will filter the picklists
crmForm.FilterPicklist = function x()
{
var oPrimaryPicklist = crmForm.all.new_crimperbrand2;
var oRealatedPicklist = crmForm.all.new_crimpermodel2;

if (oPrimaryPicklist.DataValue == null)
{
oRealatedPicklist.DataValue = null;
oRealatedPicklist.ForceSubmit = true;
oRealatedPicklist.Disabled = true;


return;
}


var oTempArray = new Array();

var iLength = oRealatedPicklist.originalPicklistOptions.length;

var aCurrentType = new Array();


switch (oPrimaryPicklist.DataValue)
{
case "1":
aCurrentType = new Array(1,2,3);
break;
case "2":
aCurrentType = new Array(4,5);
break;
case "3":

break;
case "4":

break;
case "5":

break;
case "6":

break;
case "7":

break;
case "8":

break;
case "9":

break;
}


for (var i = 0; i < iLength; i++)
{
if (aCurrentType.Contains(oRealatedPicklist.originalPicklistOptions[i].DataValue))
{
oTempArray.push(oRealatedPicklist.originalPicklistOptions[i]);
}
}

oRealatedPicklist.Options = oTempArray;

if (oTempArray.length > 0)
{
oRealatedPicklist.Disabled = false;
}
else
{
oRealatedPicklist.DataValue = null;
oRealatedPicklist.ForceSubmit = true;
oRealatedPicklist.Disabled = true;
}
}

var CRM_FORM_TYPE_CREATE = 1;
var CRM_FORM_TYPE_UPDATE = 2;

switch (crmForm.FormType)
{
case CRM_FORM_TYPE_CREATE://CREATE_FORM - try these and blow away the vars above
case CRM_FORM_TYPE_UPDATE: //UPDATE_FORM

var oRealatedPicklist = crmForm.all.new_crimpermodel2;

oRealatedPicklist.originalPicklistOptions = oRealatedPicklist.Options;

if (crmForm.all.new_crimperbrand2.DataValue == null)
{
oRealatedPicklist.Disabled = true;
}
else
{
var iPicklistValue = oRealatedPicklist.DataValue;
crmForm.FilterPicklist();
oRealatedPicklist.DataValue = iPicklistValue;
}

break;
}

Friday, April 03, 2009 8:13:00 AM

 
Anonymous Anonymous said...

Hi Mark

Excellent work by you and Greg.I was stuck into this problem for last many weeks. Thanks a lot. I can't even imagine of such a simplest script. You guys rock!!

Monday, July 06, 2009 8:49:00 AM

 

Post a Comment

<< Home