Sunday, April 12, 2009

CRM 4.0 Many 2 Many IFrame Viewer

I’ve seen many posts about displaying a many 2 many grid inside an iframe so I didn’t post about it my self until now.

One of the disadvantages of using such customization is that it does not follow ms initial execution path which breaks some of the functionality.

In our case after you select an existing item the grid does not refresh / reflect your selection until you push the refresh button your self.

I wrote a small utility class that solves that problem. It also takes care of the iframe padding.

Basically a many to many iframe looks like this:

areas.aspx?oId={ObjectId}&oType={ObjectTypeCode}&security=852023&roleOrd=1&tabSet=area{relationship name}

The viewer accepts the RoleOrder and Relationship Name (TabsetId) and constructs the iframe for you.

Add the code in the entity onload event and Enjoy…


function OnCrmPageLoad()
{
/* Create a N2NViewer and give it the IFRAME (container) id */
var n2nViewer = new N2NViewer('IFRAME_account_association');
/* Set the role order - use iedevtoolber for exact parameters */
n2nViewer.RoleOrder = 1;
/* assing the relationship name (without the "area" word) */
n2nViewer.TabsetId = "gi_account_account";
/* Do the trick... */
n2nViewer.Load();
}

function N2NViewer(iframeId)
{
if (!document.all[iframeId])
{
alert(iframeId + " is missing!");
return;
}

var viewer = this;
var _locAssocObj = null;

viewer.IFRAME = document.all[iframeId];
viewer.RoleOrder;
viewer.TabsetId;

viewer.Load = function()
{
/* Construct a valid N2N IFRAME url */
viewer.IFRAME.src = "areas.aspx?oId=" + crmForm.ObjectId + "&oType=" + crmForm.ObjectTypeCode + "&security=" + crmFormSubmit.crmFormSubmitSecurity.value + "&roleOrd=" + viewer.RoleOrder + "&tabSet=area" + viewer.TabsetId;
viewer.IFRAME.onreadystatechange = viewer.StateChanged;
}

viewer.StateChanged = function()
{
if (viewer.IFRAME.readyState != 'complete')
{
return;
}

var iframeDoc = viewer.IFRAME.contentWindow.document;

/* Reomve scrolling space */
iframeDoc.body.scroll = "no";
/* Remove crmGrid Default padding */
iframeDoc.body.childNodes[0].rows[0].cells[0].style.padding = 0;

/* Save MS locAssocObj */
_locAssocObj = locAssocObj;
/* Override MS locAssocObj */
locAssocObj = viewer.locAssocObj;
}

viewer.locAssocObj = function(iType , sSubType, sAssociationName, iRoleOrdinal)
{
/* Open the Dialog */
_locAssocObj(iType , sSubType, sAssociationName, iRoleOrdinal);
/* Refresh only if our iframe contains the correnct tabset name */
if (sAssociationName == viewer.TabsetId)
{
viewer.IFRAME.contentWindow.document.all.crmGrid.Refresh();
}
}
}

//Entry Point
OnCrmPageLoad();

18 comments:

Anonymous said...

Dude.. it works..
thank you so much...

I've spent the last years trying to figure this one out and come up with nothing (duh)

Anonymous said...

it does not work with opp. product associated view. the grid does not get refreshed ?

Adi Katz said...

This means the condition in line 60 is not met.
Insert an alert(sAssociationName + “=” + viewer.TabsetId);
If the result is not the same the grid won’t get refreshed.

Anonymous said...

Hi Adi,

Just a simple question. I have created 2 iFrames inside quotes entity. Everytime I hav to click the refresh button wen I create a new record inside the iFrame.How to automate the refresh button here?

Adi Katz said...

This code automates the refresh for a ‘many to many’ iframe.

If you’re displaying a 1 to many iframe take a look at ‘Display Fetch in IFRAME’ post in my blog.

Anonymous said...

Thanx a lot Adi... Appreciate!!

Anonymous said...

Hi Adi,

I created three IFrames and displays the N:N relationship. The code executed successfully but the grid cannot be refreshed.

Adi Katz said...

Which entity are you trying to refresh. There might be differences between entities. Some use the locAssocObj some use other similar functions. You should put an alert in the locAssocObj and see if it fires. If not then you should lookup the function that is fired for the entity and implement it in the same way I implemented the locAssocObj

Anonymous said...

Hi Adi,
Thank you for this very informative post! I am very impressed with how you redirect the MSFT function call, as I would like to use this to run other functions as well on Associate.

I'm about to check out hooking into the Remove as well.

Is it safe to say that modifications of this sort are unsupported??

Thanks again,
Aron

Anonymous said...

Hi Adi,
I tried hooking into the Remove Menu Item, which executes doActionEx(sGrid, iObjType, sParentId, sAction, iParentType, sParams). For some reason I was unable to reproduce the same result as you demonstrated with locAssocObj.

Do you know if it is even possible?

Thanks,
Aron

Adi Katz said...

Tell me which entity is implementing this code and I’ll check whether the code also works with your requirement.

Anonymous said...

It is a custom entity that I created, related to another custom entity via N:N.
Thanks,
Aron

Bradl said...

I have everything working visually. I have my entity and the related entity displaying on the form. When I select "add new" I am able to however it does not appear in the grid view?

I am unsure how to troubleshoot it via Line 60 as mentioned above where you mention alerts for line 60 or verifying the "locAssocObj"

Any help is greatly appreciated, this is the best implementaiton of this I have seen, I just want to use it over the simple ones. THANKS!

Bradl said...

I am doing this on a custom entity as well am getting nothing. I dud an alert on "locAssocObj" and I got this:

---------------------------
Message from webpage
---------------------------
function(iType , sSubType, sAssociationName, iRoleOrdinal)

{

/* Open the Dialog */

_locAssocObj(iType , sSubType, sAssociationName, iRoleOrdinal);

/* Refresh only if our iframe contains the correnct tabset name */

if (sAssociationName == viewer.TabsetId)

{

viewer.IFRAME.contentWindow.document.all.crmGrid.Refresh();

}

}
---------------------------
OK
---------------------------

Anonymous said...

Hi Adi,

Is there a way to add filter conditions to the IFRAME we created?

thanks in advance,
Eran

Tanguy said...

Hi Adi,

Can't succeed in making this NN Grid to refresh automatically whereas the function called by the "add existing" is locAssocObj.

What can I do? Can you email me directly (my email is on the CRM MVP mailing list).

Thank you

Katie said...

Hi Adi,

The code works, but IE seems to lock up every now and then because of it. Do you now why this is?

Putting any alert in the onload event seems to fix the issue, so it seems like some sort of loading/timing delay issue. Have you experienced this before?

I think it only started happening recently (last week or so), but I'm not 100% sure.

Thanks
Katie

sbudyak said...

Hi, thanks for the code. But I have the strange limitations. I created n-to-n realationship between account and contacts to bind accounts and their contact persons. When i create an iframe in account form with related contacts, everything is fine. But when i created the iframe on the contact form with related accounts - it is empty. When i selecting the "Related accounts" from the left navigation bar i can see the related accounts. Can you help me to understand where is the problem.