Javascript Check if MS Form in Iframe was submitted - javascript

I have a Web based Training where we'd like to embed an MS-Form as an Iframe into a page and via Javascript would like to enable the next page when the user has submitted the form.
Is there any way possible to check if the MS-Form inside the iframe was submitted?
I tried by getting the iframe via an assigned ID, but it is not possible to access the iframes #document (security cross-origin reason) and for example check buttons.
Maybe there are some messages that can be posted to the contentWindow?
I've also seen, that there is no API that would support checking if I've already submitted a certain form. As far as I have seen it is only possible to access your own forms via API.

You can use the PostMessage api to listen/send events from and to the iframe.
https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/scenarios/postmessage
You can listen to events by adding this event listener after the iframe has loaded:
function handlePostMessage(e) {
// The actual message is contained in the data property of the event.
var msg = JSON.parse(e.data);
// The message ID is now a property of the message object.
var msgId = msg.MessageId;
// The message parameters themselves are in the Values
// parameter on the message object.
var msgData = msg.Values;
// Do something with the message here. }
window.addEventListener('message', handlePostMessage, false);

Related

How to pass data from html web resource to dynamics 365 ce

I want to pass data from my dynamic html table on button click event to model driven app account form in dynamic 365 crm. But I'm not be able to pass the data. Please refer the below java script function that I used to pass data.
function addAddress(event) {
var row = $(event);
var parent = row.parent();
parent = parent.parent();
window.parent.Xrm.Page.getAttribute("address1_line1").setValue(parent.closest("tr").find('td:eq(0)').text());
window.parent.Xrm.Page.getAttribute("address1_line2").setValue(parent.closest("tr").find('td:eq(1)').text());
window.parent.Xrm.Page.getAttribute("address1_line3").setValue(parent.closest("tr").find('td:eq(2)').text());
window.parent.Xrm.Page.getAttribute("address1_city").setValue(parent.closest("tr").find('td:eq(3)').text());
window.parent.Xrm.Page.getAttribute("address1_stateorprovince").setValue(parent.closest("tr").find('td:eq(4)').text());
window.parent.Xrm.Page.getAttribute("address1_country").setValue(parent.closest("tr").find('td:eq(6)').text());
}
One simple option is to refactor your code to use the postMessage() function. Once that button is clicked on your HTML WebResource, you can post a message that can be handled by the form which can take action based on that.
As an example, when X event is triggered on your HTML WebResource, the following code sends a message (you can format your object whatever you like) to the parent window (account form):
// Please change the targetOrigin parameter
window.parent.postMessage({ "line1": "value1", "line2": "value2"}, "*");
On your account form JS WebResource, the following code registers an event handler that will listen for your message and take action using the _formContext object which can be saved on the onLoad event (as a replacement of Xrm.Page which has been deprecated):
window.addEventListener('message', function (event) {
// Important. Only accept messages from trusted origins
if (event.origin === "XXX") {
var messageData = event.data;
_formContext.getAttribute("address1_line1").setValue(messageData.line1);
_formContext.getAttribute("address1_line2").setValue(messageData.line2);
}
});
It's important that you use the postMessage and addEventListener methods on the correct window object so if the above code doesn't work as is (I've just taken the Mozilla documentation code and changed it to fit your scenario) please review this, as you might need to change window.addEventListener to window.parent.addEventListener.

Javascript PostMessage with JSON object as data

I have to post JSON object as data using javascript PostMessage method, below is the object that I would like to post:
{
"Type" : "Login"
}
I believe this method is used to between iframe implemented inside a website, the iframe source URL is different from the parent window, how do I implement it in code?
I have this sample code I used, but I don't understand how to it work and how can I test it. Appreciate if someone can help!!
Code:
#{
string jsonParameter = Newtonsoft.Json.JsonConvert.SerializeObject("Login");
}
var jsonData = "#Html.Raw(jsonParameter)";
parent.postMessage({ "Type": jsonData }, "*");
You are correct, PostMessage is used to send messages between ifames. The code you have given in your sample is intended to be used in a child iframe and it is sending a message to the parent iframe.
In the parent iframe You also need to listen for messages coming in, for example
window.addEventListener("message", (event) => {
console.log("Received data from child iframe ", event.data);
});
Assuming you are sending a static message, the code sample you have given that sends a message to the parent iframe can also be simplified as there is no need for the c#
parent.postMessage({ "Type": ""Login }, "*");
Please also note the second parameter to PostMessage should be an expected domain instead of "*" as it prevents malicious sites from intercepting messages. I would recomend reading the mdn page for more information https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

CRM and iframe aspx page form submission

Scenario :
I have aspx page which I need to Iframe on CRM's Opportunity form. This aspx page has form which submits data into the other database.
Requirement :
I would like when user clicks save button on CRM opportunity form ,aspx page should store the data in external database and opportunity form should also save all the changes on CRM form.
My Efforts :
Till now I have Iframed aspx page on CRM form.I am also submitting the form using OnSave event.
But the only problem is the form gets submitted but by the time it executes the complete code CRM form gets refreshed . End result is that Data on aspx page does not get stored in the external database.
What can be the other possible way to achieve this functionality ?
Thanks for taking time to read. Thank you in advance.
Option 1: The better solution is to do this from an opportunity post event plug-in. This ensures data consistency between CRM and external data (if required). Also you could use WCF or a web service to transmit the data to external DB.
Option 2: If you must use javascript you could (1) bind to opportunity form OnSave, (2) Prevent the form from submitting , (3) submit the iframe and (4) wait until it comes back and then (5) do another save to complete the action. This however might cause inconsistencies between CRM and external DB if opportunity save fails.
Here is a pseudo code example
function OpportunityOnLoad() {
IFRAME.OnReadyStateChange = function() {
// (4) Check success if possible
// (5) unbind save event and complete the opportunity save
Form.RemoveOnSave(OpportunityOnSave)
Form.Save();
}
//OnLoad
Form.AddOnSave (OpportunityOnSave);
}
function OpportunityOnSave(context) {
//(1) Save clicked
//(2) Stop save
context.PreventDefault();
//(3) Submit iframe form
IFRAME.Submit();
}
EDIT:
Regarding Q1 : unfortunately not.
Regarding Q2 :
This is a rough translation of the concept above into Javascript and CRM client side API.
I didn’t test it but it should put you on the right track.
Change the Params to match the iframe id, url etc.
also since you’re using an aspx you might experience cross domain issue that could be easily overcome if you’re browsing IE and not so easily overcome if you’re using CROME for example.
var IFRAME, SaveMode;
var FORM = Xrm.Page.data.entity;
var UI = Xrm.Page.ui;
var SaveModes = {
1 : "save",
2 : "saveandclose",
59: "saveandnew"
}
var Params = {
IframeBaseUrl : "",
IframeId : "IFRAME_test",
IframeFormId : "form1"
}
function OpportunityOnLoad() {
var sUrlparams = "?"; //add required params after ?
var IframeUrl = Params.IframeBaseUrl + sUrlParams;
IFRAME = UI.controls.get(Params.IframeId);
IFRAME.setSrc(IframeUrl);
IFRAME.Dom = document.getElementById(Params.IframeId);
IFRAME.add_readyStateComplete(OnAfterIfameSave);
FORM.addOnSave(OpportunityOnSave);
}
function OnAfterIfameSave() {
//SubmitSuccess indicates that the form has reloaded after a
//successful submit. You'll need to set this variable inside your iframe.
if (IFRAME.contentWindow.SubmitSuccess) {
FORM.removeOnSave(OpportunityOnSave);
FORM.save(SaveModes[SaveMode]);
}
}
function OpportunityOnSave(execObj) {
var evArgs = execObj.getEventArgs();
evArgs.preventDefault();
SaveMode = evArgs.getSaveMode();
IFRAME.contentWindow.document
.getElementById(Params.IframeFormId)
.Submit();
}

Send a value from a cookie to a script

A user input form on my site sends values to a script located on a different server:
<form action="https://mycompany.formprocessor.com/script/" method="post" name="MyForm" onsubmit="return myFunction();"> etc.
I am recreating that form with a modified functionality. Instead of sending the form values directly, I save them in a cookie, and only then, when a certain event happens, I want to send input values from that cookie to the script located at the url provided as the value for action attribute.
Something like this:
on event {
// send cookie value to https://mycompany.formprocessor.com/script/ using "post" method;
}
How can this be done?
First of all, you will need to use some cookie functions to store the values in cookie(s) and then access the input values you have saved in those cookie(s). Some cookie functions with explanation how to use them are here: http://www.codelib.net/javascript/cookies.html
Then you will need to have a way of sending the data across domains. To do that I suggest that you use a form containing a hidden field and then submit that form to the other server.
Like this:
<form method="post" action="https://mycompany.formprocessor.com/script/">
<input type="hidden" id="hiddendata" name="data" />
</form>
<script>
function onEvent() { /* call this function when your desired event happens */
var cookiedata = readCookie('cookiename');
var datafield = document.getElementById('hiddendata');
datafield.value = cookiedata;
datafield.form.submit();
}
</script>
Note that I suggest you have the program at formprocessor return 204 No Content as its HTTP status code so that the document itself is not reset. Otherwise the browser will replace the current page with the contents of https://mycompany.formprocessor.com/script/ after the data is submitted. This is the a way of doing AJAX that does not block cross-domain requests.
The following method will only work if you are content with using GET requests to send the data to the server.
You can set the src attribute of included elements on the page, like an img element, to a URL like this (initial image is from http://commons.wikimedia.org/wiki/File:1x1.png)
<img id="hiddenimage" width="1" height="1" src="1x1.png" />
<script>
function onEvent() { /* call this function when your desired event happens */
var cookiedata = readCookie('cookiename');
var dataimage = document.getElementById('hiddenimage');
dataimage.src = 'https://mycompany.formprocessor.com/script/?data=' + encodeURIComponent(cookiedata);
}
</script>
First of all, you will need to use some cookie functions to store the values in cookie(s) and then access the input values you have saved in those cookie(s). Some cookie functions with explanation how to use them are here: http://www.codelib.net/javascript/cookies.html
Then you will need to have a way of sending the data across domains. To do that I suggest that you use a form containing a hidden field and then submit that form to the other server.
Like this:
<script>
function onEvent() { /* call this function when your desired event happens */
var cookiedata = readCookie('cookiename');
var datafield = document.getElementById('hiddendata');
datafield.value = cookiedata;
datafield.form.submit();
}
</script>
Note that I suggest you have the program return 204 No Content as its HTTP status code so that the document itself is not reset. Otherwise the browser will replace the current page with the contents of https://mycompany.formprocessor.com/script/ after the data is submitted. This is the a way of doing AJAX that does not block cross-domain requests.
I am about to describe another way of sending cross-domain requests without submitting a form... please wait.
The following method will only work if you are content with using GET requests to send the data to the server.
You can set the src attribute of included elements on the page, like an img element, to a URL like this (initial image is from http://commons.wikimedia.org/wiki/File:1x1.png)
<img id="hiddenimage" width="1" height="1" src="1x1.png" />
<script>
function onEvent() { /* call this function when your desired event happens */
var cookiedata = readCookie('cookiename');
var dataimage = document.getElementById('hiddenimage');
dataimage.src = 'https://mycompany.formprocessor.com/script/?data=' + encodeURIComponent(cookiedata);
}
</script>
You can get the values set in the cookie by using "document.cookie".
Update:
var cookievalue= document.cookie;
var xmlhttp= new XMLHttpRequest();
var url="https://example.com/path?value="+cookievalue;
xmlhttp.open("POST",url,false);
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
xmlhttp.send();
The answer kind of depends on how the script that's accepting the values is set up. For storing and retrieving the values, balaji's suggestion is good enough. The plugin located here makes storing and retrieving values to/from cookies very easy.
So, for e.g. if the function you are sending the values to is something like this:
public void myFunction(firstValue, secondValue){
// logic here
}
then the way you'd send the values is something like this:
https://mycompany.formprocessor.com/script/firstValue="value_from_cookie"?secondValue="value_from_cookie"

Iframe onload does not execute after file download?

I show a waiting dialog and then i create an iframe with the url of the file and append it to the document.
Neptune.ShowWaitingDialog();
iFrame.src = "ExportReportAllMediaDetailsCsv/?" + $.param(object);
iFrame.style.display = "none";
iFrame.onload = function () {
parent.Neptune.CloseWaitingDialog();
};
document.body.appendChild(iFrame);
If there is an error on the server side the iFrame.onload function executes accordingly and the waiting dialog is closed, however if a file is returned the onload function doesnt get executed and the waiting dialog stays open.
my question is, if a file is returned doesnt the iframe get refreshed and hence cause the onload event to execute?
if no then is there a way to detect if a file has been returned?
The way I do that, taken from T.J. Crowder's answer to this old question of mine, is this:
Add a hidden form field or GET parameter called "nonce" (or whatever you want). Fill it with a unique number or random string, either at page creation time or with JavaScript.
On the server, look for the "nonce" parameter and add a cookie called "FILE_READY" (or whatever you want), and set its value to the nonce value. Return the file as you normally would.
On the client, when the form posts or the iframe "src" is set — in other words, when you initiate the file download — start up a JavaScript interval timer to check the current value of the "FILE_READY" cookie. Check like every 100 milliseconds or so; it doesn't have to be super-frequently.
As soon as the "FILE_READY" cookie has the value sent in the "nonce" parameter, then you know that the HTTP response has come back from the server.
Works like a charm.

Categories

Resources