Thursday, March 12, 2009

Display Fetch in IFRAME – Part 2


My first post about displaying fetch inside an IFRAME has two disadvantages. The first is that the context of the parent window does not follow to the new entity window when you click on add new record (grid) button. This is might be annoying if you’re counting on the default mapping between the two entities. The second problem is that the grid does not refresh after you add (new button) or remove (delete button) a record. This means you are not able to see the changes unless you press on the grid refresh button your self.

I made piece with the fact that the parent context did not follow through to the child window since I was using advanced find. But the fact that the gird does not refresh automatically came as a surprise since I remember this feature working “AS IS” in v3.0. I guess ms did a few changes to the auto function which searched and refreshed the grid. Thanks to Dave Berry who commented / Posted about this and brought it to my attention.

In order to overcome this issue I added two new properties to the FetchViewer class.

The first parameter is called WithParentContext. This property allows you to decide whether the parent window object id is passed to the child window. I made it a public (as opposed to internal) choice since I figured you can display any type of query inside the FetchViewer including a query that does not reflect a direct parent child relationship.

The second parameter is the EntityCode (ObjectTypeCode) of the entity being fetched. If you want the grid to refresh automatically you must supply this value. This is important since the value is eventually passed to MS original auto function which expects this value.

The rest of the fetch viewer class was not changed and you can read all about it here.


function OnCrmPageLoad()
{
window.fetchContacts = new FetchViewer("IFRAME_test");
fetchContacts.WithParentContext = true;
fetchContacts.EntityCode = 2;
fetchContacts.FetchXml = getFetchXml();
fetchContacts.LayoutXml = getLayoutXml();
fetchContacts.Entity = "contact";
fetchContacts.QueryId = "{00000000-0000-0000-00AA-000000666400}";
fetchContacts.RegisterOnTab(0); //IFRAME ON THE FIRST TAB
}

function getFetchXml()
{
return '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"><entity name="contact"><attribute name="fullname"/><attribute name="telephone1"/><attribute name="contactid"/><order attribute="fullname" descending="false"/><filter type="and"><condition attribute="parentcustomerid" operator="eq" uitype="account" value="' + crmForm.ObjectId + '"/></filter></entity></fetch>';
}

function getLayoutXml()
{
return '<grid name="resultset" object="2" jump="lastname" select="1" icon="1" preview="1"><row name="result" id="contactid"><cell name="fullname" width="300" /><cell name="telephone1" width="125" /></row></grid>';
}

function FetchViewer( iframeId )
{
var Instance = this;
var vDynamicForm;
var m_iframeTab;
var m_iframeDoc;
var m_iframeShowModalDialogFunc = null;
var m_windowAutoFunc = null;

Instance.WithParentContext = false;
Instance.EntityCode = 0;
Instance.Entity = "";
Instance.Iframe = null;
Instance.FetchXml = "";
Instance.QueryId = "";
Instance.LayoutXml = "";

Instance.RegisterOnTab = function( tabIndex )
{
Instance.Iframe = document.getElementById( iframeId );

if( !Instance.Iframe )
{
return alert( "Iframe " + iframeId + " is undefined" );
}

m_iframeDoc = getIframeDocument();
var loadingGifHTML = "<table height='100%' width='100%' style='cursor:wait'>";
loadingGifHTML += "<tr>";
loadingGifHTML += "<td valign='middle' align='center'>";
loadingGifHTML += "<img alt='' src='/_imgs/AdvFind/progress.gif'/>";
loadingGifHTML += "<div/><b>Loading View...</b>";
loadingGifHTML += "</td></tr></table>";
m_iframeDoc.body.innerHTML = loadingGifHTML;

if( parseInt( "0" + tabIndex ) == 0 )
{
Instance.Refresh();
}
else
{
Instance.Iframe.attachEvent( "onreadystatechange" , RefreshOnReadyStateChange );
}
}

function RefreshOnReadyStateChange()
{
if( Instance.Iframe.readyState != 'complete' )
{
return;
}

Instance.Refresh();
}

Instance.Refresh = function()
{
if( !Instance.Iframe )
{
return alert( "Iframe " + iframeId + " is undefined" );
}

m_iframeDoc = getIframeDocument();

Instance.Iframe.detachEvent( "onreadystatechange" , RefreshOnReadyStateChange );

var create = m_iframeDoc.createElement;
var append1 = m_iframeDoc.appendChild;
vDynamicForm = create("<FORM name='vDynamicForm' method='post'>");

var append2 = vDynamicForm.appendChild;
append2(create("<INPUT type='hidden' name='FetchXml'>"));
append2(create("<INPUT type='hidden' name='LayoutXml'>"));
append2(create("<INPUT type='hidden' name='EntityName'>"));
append2(create("<INPUT type='hidden' name='DefaultAdvFindViewId'>"));
append2(create("<INPUT type='hidden' name='ViewType'>"));
append1( vDynamicForm );

vDynamicForm.action = "/" + ORG_UNIQUE_NAME + "/AdvancedFind/fetchData.aspx";
vDynamicForm.FetchXml.value = Instance.FetchXml;
vDynamicForm.LayoutXml.value = Instance.LayoutXml;
vDynamicForm.EntityName.value = Instance.Entity;
vDynamicForm.DefaultAdvFindViewId.value = Instance.QueryId;
vDynamicForm.ViewType.value = 1039;
vDynamicForm.submit();

Instance.Iframe.attachEvent( "onreadystatechange" , OnViewReady );
}

function OnViewReady()
{
if( Instance.Iframe.readyState != 'complete' )
{
return;
}

Instance.Iframe.style.border = 0;
Instance.Iframe.detachEvent( "onreadystatechange" , OnViewReady );

if (Instance.WithParentContext == true)
{
getIframeWindow().open = OnWindowOpen;
}

if (m_iframeShowModalDialogFunc == null)
{
m_iframeShowModalDialogFunc = getIframeWindow().showModalDialog;
getIframeWindow().showModalDialog = OnIframeShowModalDialog;
}

if (Instance.EntityCode > 0)
{
if (m_windowAutoFunc == null)
{
m_windowAutoFunc = window.auto;
window.auto = OnWindowAuto;
}
}

m_iframeDoc = getIframeDocument();
m_iframeDoc.body.scroll = "no";
m_iframeDoc.body.style.padding = "0px";
}

function OnWindowOpen(url, name, features)
{
//new window
if (url.indexOf('?') == -1)
{
if (url.indexOf('userdefined') == -1 )
{
url = url + "?_CreateFromType=" + crmForm.ObjectTypeCode + "&_CreateFromId=" + crmForm.ObjectId;
}
else
{
url = url + "?_CreateFromType=" + crmForm.ObjectTypeCode + "&_CreateFromId=" + crmForm.ObjectId + "&etc=" + Instance.EntityCode + "#";
}
}

return window.open(url, name, features);
}

function OnIframeShowModalDialog(sUrl, vArguments, sFeatures)
{
m_iframeShowModalDialogFunc(sUrl, vArguments, sFeatures);
Instance.Refresh();
}

function OnWindowAuto(otc)
{
if ( otc == Instance.EntityCode )
{
getIframeDocument().all.crmGrid.Refresh();
}

m_windowAutoFunc(otc);
}

function getIframeDocument()
{
return getIframeWindow().document;
}

function getIframeWindow()
{
return Instance.Iframe.contentWindow;
}
}

OnCrmPageLoad();

56 comments:

Dave Berry said...

PingBack from http://6ix4our.blogspot.com/2009/03/microsoft-crm-embedding-advanced-find_25.html.

Dave Berry said...
This comment has been removed by the author.
Dave Berry said...

Adi,

Change your function below as I have updated:

function OnIframeShowModalDialog(sUrl, vArguments, sFeatures) {
var returnVar = m_iFrameShowModalDialogFunc(sUrl, vArguments, sFeatures);
if (sUrl.search(/OnDemandWorkflow/) < 0) {
Instance.Refresh();
}
return returnVar;
}

I've made this update in my code as well. It allows the "Run Workflow" button to work.

Anonymous said...

Hi Adi,

How to make this code dynamic? I want specific records in the iframe only. Currently by using the fetch xml I'm getting only a specific record always, the one which I gave in AF. How to get other records?

Adi Katz said...

What other records are you interested in? what are you trying to achieve

the code is dynamic since it uses the crmForm.ObjectId to retrieve the records.

Anonymous said...

Hi Adi,

Thanx for the update, I had ignored that part. Its working now with dynamic values.

I have put a workflow inside the iFrame entity and its not working when I see from the iFrame but I can see it working from the main record page? I have even incorporated Dave's commented code for workflows into mine but no rescue. Any clue?

I want to put a fetch and auto refresh for quote detail, how can i achieve this? Because quote details is natively integrated with quotes it doesnt show up in AF also.

Adi Katz said...

If you need to show quote details for a single quote then you need to implement the quote associated view. If you’re looking to display multiple quote details from different quotes the only solution is to write a report and show that in you iframe. Of course you won’t be able to easily use the workflow button.

Anonymous said...

Thanx for the prompt response.

Wat i discovered now is that watever fields I am filling inside the iFrame is not seen once I save & close it and open it again. But the fields are showing in the view part. I can see the filled records from the main page of the entity. Wer am I going wrong?

Yes I need to show quote details for a single quote.I have created the iFrame inside the quote entity for quote details. How can i put a auto refresh to it?

Anonymous said...

Since I can see the "view" with data but the attributes inside the form are empty I guess I should change the URL inside the iFrame, not sure.... I'm sure i'm missing somethng here...

Adi Katz said...

I wrote a new post about showing an associated view in an iframe. It also handles the refresh button.

Anonymous said...

can anyone provide me some snippet to retreive the selected record from the grid to display the into the field above the IFrame.

Vishwas said...

Hi Adi,
I need few help from ur side, a I have an IFRAME I need to display the quarters from a quarters entity based on the start date and end date entered...


kindly help me..

Adi Katz said...

Wishwaz, i replied to your post on ms forums.

Kumar said...

Adi, Can you please help me on this one..I really got stuck since past couple of days..
I want to poplulate the selected record from the view to the above main forms fields..
Is there any way to do that...?

Vishwas said...

Thanks ADI for ur reply..
can u give me the link of the thread

Adi Katz said...

This is your post

Adi Katz said...

Kumar, I’m not sure I completely understand you requirement.

Please open a new thread on ms forums and I’ll try to help you out.

Kumar said...

Thanks Adi,

I have just open a thread in this forum.

Actually, with above method, we are placing the Advanced find result in grid view of contact in the Account entity form.



And from the Contact entity view, how Account entity form can retrieve which record is selected from the contact gridview that is embedded in the Account entity view.



I would like get the selected record from that grid view and display that selected row records in appropriate field of the account entity form.





Any help with some code snippet would be great..

Vishwaz said...

Thanks ADI, Its perfect and working fine for me after a long struggle just because of my stupidity....

Just for Improvement: In THE IFRAME GRID(Advanced find window that is displayed in the IFRAME) Can remove the NEW button??

Adi Katz said...

You can only remove the button by using JavaScript.
After line 144 you can insert a line similar to the following code:

m_iframeDoc.all.<id of new button>.style.display = ‘none’

Rick Basten said...
This comment has been removed by the author.
alsherin said...

Hi Adi, thank you for sharing these great code masterpieces. It works fine.

I have the following request: the resulting grid in an iframe is the same as in the advancedfind window, including grid menubar and its functionality. Can I somehow change it to the one used in associated grid view?

I can show associated grid view in an iframe, but I need to show a tabset based on advanced find query, that can be achieved with your fetch an an iframe code example. However, its funcionality is not sufficient for me.

Is it possible at all?

Adi Katz said...

Hi Alex, What functionality of the associated view do you need to see in the fetchviewer? The toolbars look almost the same? Which entity’s associated view are you referring to?

alsherin said...

Hi Adi, thank you for quick response. I am working with custom entities. Associated grid menubar has the following buttons: (1) Create new object ObjectName; (2) Add existing object ObjectName. The one in advanced find view has only one button "Create new". And the grid intself behave differently: when I doubleclick a record I get a form to create new object instead of editing form for selected object.

alsherin said...

I was trying to find a solution myself by reverse engeneering the code behind for fetchdata.aspx and stuck at CofigurePage method of the inherited class FetchData, which set protected field crmGridMenuBar to GridTypeCode.AdvancedFind (this.crmGridMenuBar.GridType = GridTypeCode.AdvancedFind;).
Is there a way to override this setting?

Anonymous said...

I got this working but now I am trying to make this work for a custom entity. I changed the OnCrmPageLoad params, the getFetchXml and the getLayoutXml sections. For some reason it still thinks I am using the contacts. I am getting a "Query Builder Error - not attribute". When I look in the event viewer it is trying to find my key field in contacts. I can't figure out why it still thinks I am using the contacts. Any idea?

Anonymous said...

Nevermind on previous post. I forgot to change my queryid.

Anonymous said...

I modified the OnIframeShowModalDialig function per Dave Berry's post to get the workflow to work but now I get an error "A Microsoft Dynamics CRM window was unable to open, and may have been blocked by a pop-up blocker."

Unknown said...

Hi Adi,

Thanks for your codes. It is working. But I cannot make the "order" working. For example I change order attribute="name" descending="false"/ to order attribute="createdon" descending="true"/.

Would you have a look?

Thanks
Andrew

Unknown said...

Hi Adi,

I want to make a dynamic view, which is base a picklist value on the form. I changed the condition to use the value in getFetchXml().
Then I try to reload the Iframe when the picklist is changed. I put all your code in the onchange event. But it is not working, it keep showing "Leading View...".

Please help, thanks
Andrew

Adi Katz said...

The code should run in the onload event.

The onchange only needs to reassign the fetchxml with the new picklist value e.g. fetchContacts.FetchXml = getFetchXml(); and call the refresh method e.g. fetchContacts.Refresh();

Chad Marais said...

Hi Adi

I'm REALLY hoping you can help me out here! I've been directed to your blog by some of the guys at microsoft (after contacting them for help they were unable to come up with any suggestions and pointed me in the direction of this post - however, this solution also doesn't cover exactly what I need to do - its similar, but wont work given that I cant use an entity form or an iframe for my solution)

I was wondering if I'd be able to contact you on email / your blog to give you a quick overview of what I've been asked to code and to hear what you suggest would be the best to achieve this?

I've hit nothing but brick walls for the last 3 weeks so any input would be greatly appreciated.

Kind regards,

Chad

Adi Katz said...

Hi Chad,

You can send the technical overview to support@gicrm.com
we'll have a look and advise if possible.

Does it by any chance related to the following thread?

http://social.microsoft.com/Forums/en-US/crmdevelopment/thread/0a9532a6-3bc8-4942-b84b-b67aaa41fbc9

Chad Marais said...

Hi Adi

Thank you for the quick response - It is EXACTLY that topic :) however, I'm going to resend the query to you as we've made a little progress - I'd like to outline where we are at the moment - what has worked and what hasn't worked - very interested to hear your take on it - I've just had another response for MS saying that they have no more ideas as to how this can be solved - which I find hard to believe!?

Will email you shortly! thanks so much for taking the time! its greatly appreciated

malenkii_prinz said...

Hi, Adi!

I'm trying to display Notes (annotation entity - entitycode 5) in IFRAME fetch, but i did not succeed. It has no system default VIEW. So i'm getting error messages. I was trying to create Advanced View and change the viewtype in your code, but it did not help ( is it possible to solve the problem?

Adi Katz said...

The following post displays notes inside an iframe: http://mscrm4ever.blogspot.com/2008/10/playing-with-notes.html

Good luck

malenkii_prinz said...

Great thanx to you Adi =)
If its not a secret, how did you find the QueryId for annotation ( {DDDFF6AE-2F52-4640-B2BB-2BA59DA0777C})?

I need to know if i'd like to display some other entities that don't have interface view.

malenkii_prinz said...

One More question: I'm trying to add the button (or menu) in fetch page menu Toolbar. So i'm just append "LI" element to "UL" of the m_ifamedoc.

the appended LI element works fine if i attach onclick for examlple alert() method, but when i'm trying to attach some script function from (parent) CRM form there is an error occures while on click event.

IE Developer Tools show the following errors: Object expected and Could not complete the operation due to error 80020101.in fetchData.aspx

is there a way to attach parent form event to iframe menu button?

Adi Katz said...

To get the view id do an advanced find on the view entity and look for name contains “[entity name e.g. account] Advanced Find” then click on the view record and do a ctrl + N to open a new window with a visible address bar (URL).

Unknown said...

Hi Adi

I set these 3 parameters to the settings of my custom entity

fetchContacts.EntityCode = 10009;
fetchContacts.Entity = "NSW_deliveryitem";
fetchContacts.RegisterOnTab(3);

I used the fetchxml retrieved from an advanced find

When I use your code it displays in my iframe perfectly(after setting the RegisterOnTab), when I replace it with my own settings my Iframe is completely blank.
The advanced find definitely returns values so I can't understand it.

Help :)

Adi Katz said...

Try changing the entity name to lower case.

Nate said...

I had the same problem as Anymous above "Access is Denied", untill I used the suggestion from this posting: http://blog.customereffective.com/blog/2009/04/setting-blank-iframe-sources.html

Then it worked beautifully! thanks a lot!

Stephen said...

Ha Adi,

I tried your code, have 2 IFRAME's on 1 tab and it didn't work. Does it support multiple IFRAME's in one tab ?
Note: the tabs are not located on the first default tab.

Stephen said...

Hi Adi,

If I add the form's target to _self the views are opened in new windows.
It looks like it has something to do with the submit form...
I have tried to set the IFRAME's NAME en use this as well, but the same thing happens..

vincent said...

Hi Adi,

I try to use your code for showing email entities in lead form. However, the WithParentContext seems doesn't work with email form. I can see that the query string is different for email form. Could you help me with this? Thanks.

Anonymous said...

Hi Adi,

I also had the "Access Denied error" that Nate mentions above.

After reading the post "http://blog.customereffective.com/blog/2009/04/setting-blank-iframe-sources.html", I solved this issue by first setting the iframe.src via javascript before calling your code.

var iFrame = document.getElementById(iFrameId);
if (iFrame !=null){
iFrame.src = "about:blank";
}
else{
return;
}

Thanks for this great function !

Cheers, Geron

Anonymous said...

Has anyone come up with a solution for sorting the view bt one or more attributes. I can only get it to work with the sorting that appears to be by the order in which it was entered,

Anonymous said...

How to make this work with custom entity

Unknown said...

Great post! you have some brilliant contents!POP Displays

ziva said...

hi adi
i got this working out on my test server, but when i try to implement this on hoster crm i get an error access denied.
i'm assuming that i don't have sufficient permissions to access IIS folders because the hoster restricts the access to IIS folders (multi tenancy crm server).
could you please advise how to solve this problem
thanks

Oliver Worm said...

Hi Adi,
using your fetchViewer with an advanced find view works quite good. But we are facing a strange behavior when we want to delete entries in the grid. When deleting an entry for the first time, the grid refreshs like a charm afterwards. But deleting an entry for the second+ time does not refresh the grid any longer.
Could you please give us a helping hand?
Thank you in advance, oliver

Mike said...

Hi

How can I do the same thing in CRM 2011?

Mike

Markus said...

Hi Adi,
Hi Mike,

I am trying to the same in CRM 2011. Porting a perfectly working solution (similar to Adi's) from CRM 4 to 2011 for 3 subsequent days now.

The result: if I post the form to a new window, everything works fine. If I do the same thing targeting an iFrame, the grid runs into an error when trying to calculate the total record count.

Have you ported this iFrame solution to CRM 2011 already ?

Thx & Cheers :)

Anonymous said...

Hi Markus,
I've not tried this so can't say for sure but here's some differences between 4.0 and 2011 Adv Find requests. Mostly they are similar, but for IFrame in 2011 there is an extra query parameter; "pagemode=iframe". These two posts were captured from two different systems and for two different entities, but should give you an idea.

Good Luck.

2011 POST data:

FetchXml=%3Cfetch+version%3D%221.0%22+output-format%3D%22xml-platform%22+mapping%3D%22logical%22+distinct%3D%22false%22%3E%3Centity+name%3D%22contact%22%3E%3Cattribute+name%3D%22fullname%22%2F%3E%3Cattribute+name%3D%22parentcustomerid%22%2F%3E%3Cattribute+name%3D%22telephone1%22%2F%3E%3Cattribute+name%3D%22emailaddress1%22%2F%3E%3Cattribute+name%3D%22contactid%22%2F%3E%3Corder+attribute%3D%22fullname%22+descending%3D%22false%22%2F%3E%3Cfilter+type%3D%22and%22%3E%3Ccondition+attribute%3D%22ownerid%22+operator%3D%22eq-userid%22%2F%3E%3Ccondition+attribute%3D%22statecode%22+operator%3D%22eq%22+value%3D%220%22%2F%3E%3C%2Ffilter%3E%3C%2Fentity%3E%3C%2Ffetch%3E
&LayoutXml=%3Cgrid+name%3D%22resultset%22+object%3D%222%22+jump%3D%22fullname%22+select%3D%221%22+icon%3D%221%22+preview%3D%221%22%3E%3Crow+name%3D%22result%22+id%3D%22contactid%22%3E%3Ccell+name%3D%22fullname%22+width%3D%22300%22+%2F%3E%3Ccell+name%3D%22emailaddress1%22+width%3D%22150%22+%2F%3E%3Ccell+name%3D%22parentcustomerid%22+width%3D%22150%22+%2F%3E%3Ccell+name%3D%22telephone1%22+width%3D%22125%22+%2F%3E%3C%2Frow%3E%3C%2Fgrid%3E
&EntityName=contact
&DefaultAdvFindViewId=%7B00000000-0000-0000-00AA-000000666400%7D
&ViewId=%7B00000000-0000-0000-00AA-000010001003%7D
&ViewType=1039
&SortCol=fullname%3A1%3B
&UIProvider=Microsoft.Crm.Application.Controls.GridUIProvider
&DataProvider=Microsoft.Crm.Application.Platform.Grid.GridDataProviderQueryBuilder


2011 POST URL:

https://lab5.intoxrm.com/AdvancedFind/fetchData.aspx?EntityCode=2
&QueryId=%7b00000000-0000-0000-00AA-000010001003%7d
&ViewType=1039
&pagemode=iframe
&sitemappath=Workplace%7cMyWork%7cnav_dashboards


CRM 4 POST data:
FetchXml=%3Cfetch+version%3D%221.0%22+output-format%3D%22xml-platform%22+mapping%3D%22logical%22+distinct%3D%22false%22%3E%3Centity+name%3D%22new_district%22%3E%3Cattribute+name%3D%22new_districtid%22%2F%3E%3Cattribute+name%3D%22new_name%22%2F%3E%3Cattribute+name%3D%22createdon%22%2F%3E%3Corder+attribute%3D%22new_name%22+descending%3D%22false%22%2F%3E%3Cfilter+type%3D%22and%22%3E%3Ccondition+attribute%3D%22statecode%22+operator%3D%22eq%22+value%3D%220%22%2F%3E%3C%2Ffilter%3E%3C%2Fentity%3E%3C%2Ffetch%3E
&LayoutXml=%3Cgrid+name%3D%22resultset%22+object%3D%2210005%22+jump%3D%22new_name%22+select%3D%221%22+icon%3D%221%22+preview%3D%221%22%3E%3Crow+name%3D%22result%22+id%3D%22new_districtid%22%3E%3Ccell+name%3D%22new_name%22+width%3D%22300%22+%2F%3E%3Ccell+name%3D%22createdon%22+width%3D%22125%22+%2F%3E%3C%2Frow%3E%3C%2Fgrid%3E
&EntityName=new_district
&DefaultAdvFindViewId=%7B7E250D03-B57E-4A98-9E74-1B3EAC76D0A2%7D
&ViewId=%7BDC736EE3-D399-4007-B0DE-07086333666B%7D
&ViewType=1039
&SortCol=new_name
&SortDescend=false

CRM 4 POST URL:
https://panduit.dev.xrmlive.com/AdvancedFind/fetchData.aspx?EntityCode=10005
&QueryId={DC736EE3-D399-4007-B0DE-07086333666B}
&ViewType=1039

Anonymous said...

Hello Adi,

I have moved your working class from CRM 4 to CRM 2011. But the grid's on click event is not being fired. I want to open the Account form from the Contact Grid. Any idea whats wrong ? I have tested with WithParentContext.

ANy help appreciated.

Regards,

Furqan

Anonymous said...

I've got this code modified for my entity and it appears to be pulling up the correct Advanced Find.

But now I have a problem. The advanced find is opening on it's own page/tab in IE. It's not showing within the IFRAME that I have in Line 3 of the code.

Any ideas what could be causing this? I'm sure I'm just missing something simple I just can't find it.

Thanks so much for any help!