For to 4.0 version: http://dynamicslollipops.blogspot.com/2011/07/mscrm-40-nn-lookup-field.html
Prerequest customization:
1) 1:N relation, and lookup placed on the form.
2) N:N relationship for holding the values.
Walkthrough:
1) Create new javascript webresource with the following code:
var CRM_FORM_TYPE_CREATE = 1;
RetreiveAssociatedEntities = function(relationshipSchemaName, entity1SchemaName, entity1KeyValue, retreiveAttribute) {
var fetchXml = "<fetch mapping='logical'>"
+ " <entity name='" + relationshipSchemaName + "'>"
+ " <all-attributes />"
+ " <filter>"
+ " <condition attribute='" + entity1SchemaName + "id' operator='eq' value ='" + entity1KeyValue + "' />"
+ " </filter>"
+ " </entity>"
+ "</fetch>";
var fetchResults = Fetch(fetchXml);
var nodeList = fetchResults.selectNodes("resultset/result");
var returnList = new Array();
if (nodeList == null || nodeList.length == 0) {
return returnList;
} else {
for (i = 0; i < nodeList.length; i++) {
var idValue = nodeList[i].selectSingleNode('./' + retreiveAttribute).nodeTypedValue;
returnList[i] = idValue;
}
return returnList;
}
}
MischiefMayhemSOAP = function(serviceUrl, xmlSoapBody, soapActionHeader, suppressError) {
var xmlReq = "<?xml version='1.0' encoding='utf-8'?>"
+ "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"
+ " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
+ " xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"
+ GenerateAuthenticationHeader()
+ " <soap:Body>"
+ xmlSoapBody
+ " </soap:Body>"
+ "</soap:Envelope>";
var httpObj = new ActiveXObject("Msxml2.XMLHTTP");
httpObj.open("POST", serviceUrl, false);
httpObj.setRequestHeader("SOAPAction", soapActionHeader);
httpObj.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
httpObj.setRequestHeader("Content-Length", xmlReq.length);
httpObj.send(xmlReq);
var resultXml = httpObj.responseXML;
var errorCount = resultXml.selectNodes("//error").length;
if (errorCount != 0) {
var msg = resultXml.selectSingleNode("//description").nodeTypedValue;
if (typeof (suppressError) == "undefined" || suppressError == null) {
alert("The following error was encountered: " + msg);
}
return null;
} else {
return resultXml;
}
}
Fetch = function(fetchXml) {
var xmlSoapBody = "<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"
+ " <fetchXml>"
+ FetchEncode(fetchXml)
+ " </fetchXml>"
+ "</Fetch>";
var fetchResponse = MischiefMayhemSOAP("/MSCRMServices/2007/CrmService.asmx", xmlSoapBody, "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
if (fetchResponse != null) {
var fetchResults = new ActiveXObject("Msxml2.DOMDocument");
fetchResults.async = false;
fetchResults.resolveExternals = false;
fetchResults.loadXML(fetchResponse.text);
return fetchResults;
} else {
return null;
}
}
FetchEncode = function(strInput) //_HtmlEncode
{
var c;
var HtmlEncode = '';
if (strInput == null) {
return null;
}
if (strInput == '') {
return '';
}
for (var cnt = 0; cnt < strInput.length; cnt++) {
c = strInput.charCodeAt(cnt);
if (((c > 96) && (c < 123)) ||
((c > 64) && (c < 91)) ||
(c == 32) ||
((c > 47) && (c < 58)) ||
(c == 46) ||
(c == 44) ||
(c == 45) ||
(c == 95)) {
HtmlEncode = HtmlEncode + String.fromCharCode(c);
}
else {
HtmlEncode = HtmlEncode + '&#' + c + ';';
}
}
return HtmlEncode;
}
AssociateEntities = function(moniker1name, moniker1id, moniker2name, moniker2id, RelationshipName) {
var authenticationHeader = GenerateAuthenticationHeader();
// Prepare the SOAP message.
var xml = "<?xml version='1.0' encoding='utf-8'?>";
xml += "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance\' xmlns:xsd='http://www.w3.org/2001/XMLSchema\'>";
xml += authenticationHeader;
xml += "<soap:Body><Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'><Request xsi:type='AssociateEntitiesRequest'>";
xml += "<Moniker1><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1id + "</Id>";
xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1name + "</Name></Moniker1>";
xml += "<Moniker2><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2id + "</Id>";
xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2name + "</Name></Moniker2>";
xml += "<RelationshipName>" + RelationshipName + "</RelationshipName>";
xml += "</Request></Execute></soap:Body></soap:Envelope>";
// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xHReq.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);
// Capture the result.
var resultXml = xHReq.responseXML;
// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount != 0) {
var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
alert(msg);
}
}
DisassociateEntities = function(moniker1name, moniker1id, moniker2name, moniker2id, RelationshipName) {
var authenticationHeader = GenerateAuthenticationHeader();
// Prepare the SOAP message.
var xml = "<?xml version='1.0' encoding='utf-8'?>";
xml += "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance\' xmlns:xsd='http://www.w3.org/2001/XMLSchema\'>";
xml += authenticationHeader;
xml += "<soap:Body><Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'><Request xsi:type='DisassociateEntitiesRequest'>";
xml += "<Moniker1><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1id + "</Id>";
xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1name + "</Name></Moniker1>";
xml += "<Moniker2><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2id + "</Id>";
xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2name + "</Name></Moniker2>";
xml += "<RelationshipName>" + RelationshipName + "</RelationshipName>";
xml += "</Request></Execute></soap:Body></soap:Envelope>";
// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xHReq.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);
// Capture the result.
var resultXml = xHReq.responseXML;
// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount != 0) {
var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
alert(msg);
}
}
//returns the item location in array if not found -1
GetIndexFromArray = function(item, recordArr) {
for (var i = 0; i < recordArr.length; i++) {
if (recordArr[i] != null && recordArr[i].id == item) {
return i;
}
}
return -1;
}
/*"new_new_entity1_new_entity2new","new_multynewid", "new_entity2", "new_name"*/
FillMultiLookup = function(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName) {
var relatedValues = RetreiveAssociatedEntities(relationshipSchemaName, Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName + "id");
var value = new Array();
for (var i = 0; i < relatedValues.length; i++) {
value[i] = new Object();
value[i].id = relatedValues[i];
value[i].name = RetreiveAssociatedEntities(relatedEntitySchemaName, relatedEntitySchemaName, relatedValues[i], relatedEntityPrimaryAttributeSchemaName)[0];
value[i].typename = relatedEntitySchemaName;
}
crmForm.all[lookupSchemaName].DataValue = value;
}
UpdateN2N = function(nnId, relatedEntitySchemaName, relatedEntitySchemaId, lookupSchemaName) {
var oldValues = RetreiveAssociatedEntities(nnId, Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaId);
var value = crmForm.all[lookupSchemaName].DataValue;
//if there's records in lookup
if (value != null) {
//go over all the related records and remove them if not in the new list (lookup)
var temp = value;
for (var i = 0; i < oldValues.length; i++) {
//if not in the new list disassociate them
var index = GetIndexFromArray(oldValues[i], temp);
if (index == -1) {
DisassociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, oldValues[i], nnId);
}
else { // if in the list remove them from the list
temp[index] = null;
}
} //ends for
//go over all the remaining records and associate them
for (var i = 0; i < temp.length; i++) {
if (temp[i] != null) {
AssociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, temp[i].id, nnId);
}
}
}
else if (oldValues != null) {
for (var i = 0; i < oldValues.length; i++) {
DisassociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, oldValues[i], nnId);
}
}
}
FilterN2NLookup = function(filterByLookup, filterLookup, entityName, nnName, nnFrom, linkedEntityName, linkedAttributeName) {
var filter = '';
var values = filterByLookup.DataValue;
if (values != null) {
//filter = '<filters><filter entity="' + entityName + '"><condition attribute="' + attributeName + '" operator="in">';
//for (var i = 0; i < values.length; i++) {
// filter += '<value uiname="' + values[i].name + '">' + values[i].id + '</value>';
// }
//filter += '</condition></filter></filters>';
filter = '<link-entity entity="' + entityName + '" name="' + nnName + '" from="' + nnFrom + '" to="' + nnFrom + '" visible="false" intersect="true"><link-entity name="' + linkedEntityName + '" from="' + linkedAttributeName + '" to="' + linkedAttributeName + '"><filter type="and"><condition attribute="' + linkedAttributeName + '" operator="in">';
for (var i = 0; i < values.length; i++) {
filter += '<value uiname="' + values[i].name + '">' + values[i].id + '</value>';
}
filter += '</condition></filter></link-entity></link-entity>';
}
filterLookup.AddParam('filters', filter);
}
/*"new_new_entity1_new_entity2new","new_multynewid", "new_entity2", "new_name"*/
function ConvertN2N(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName)
{
document.getElementById(lookupSchemaName).setAttribute("lookupstyle", "multi");
document.getElementById(lookupSchemaName).setAttribute("_lookupstyle", "multi");
document.getElementById(lookupSchemaName).onchange = function(){UpdateN2N(relationshipSchemaName, relatedEntitySchemaName, relatedEntitySchemaName + "id", lookupSchemaName)};
Xrm.Page.getAttribute(lookupSchemaName).setSubmitMode("never");
if (crmForm.FormType != CRM_FORM_TYPE_CREATE) {
FillMultiLookup(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName);
}
else {
Xrm.Page.getControl(lookupSchemaName).setDisabled(true);
}
}
2) Add it to the form
3) Add to the onload
3.1) Function: ConvertN2N
3.2) Parameters: N:N Relationship name, lookup attribute name, Related Entity Schema Name, Related Entity Primary Attribute Schema Name
(Example: "new_incident_new_problem", "new_problemid", "new_problem", "new_name")
That's it, everything else is inside the code.
Plug and Play.