Passing object from one window to another window - javascript

I know this question had been asked many times in here, i tried each one of it but it just couldn't work.
I have a website application hosted under https://app.yyy.com, and another one which is basically just a receipt template, which i put inside the mine.com web server. The receipt template has its own script on it, and its URL is https://yyy.com/receipt/receiptTemplate.html. I'm telling all these details because i read that templates that are hosted under the same domain may be subjected to CORS issue when trying to pass data from one window to another.
My requirement is to pass data object from the website application to the receipt template, in which the script will get the object data and render it to the template.
I have the following in the wesbite application script, which is to make a call to fetch data, and open up the receipt template upon successful call. I also tried to attach the data by using window.opener (as suggested by one of the answer that i found in here.
$.get('/getdata', function(data) {
var invoice = window.open('https://yyy.com/receipt/receiptemplate.html');
window.opener.receiptdata = data;
});
And on the receipt script, i have the following
$(document).ready(function(){
var data = window.receiptdata;
generateReceipt(data);
});
But the above couldn't work.
I tried the following with localStorage this time, also suggested by the answer that i found in here..
$.get('/getdata', function(data) {
var invoice = window.open('https://yyy.com/receipt/receiptemplate.html');
localStorage.setItem('receiptdata', data);
});
$(document).ready(function(){
var data = localStorage.getItem('receiptdata');
generateReceipt(data);
});
But didn't work too.
I dunno how else can i pass the object data to the other page... =(

You have a typo to begin with: window in window.opener.receiptdata = data; is your current window, you want to access the window object you just created: invoice.
$.get('/getdata', function(data) {
var invoice = window.open(generateURL(), '');
invoice.receiptdata = data;
}
As a fiddle since StackSnippets won't allow popups.
But for this to work, since you are hosting the two pages on different subdomains, you may need to change their origin:
document.domain = 'yyy.com';
Now, if these were hosted on completely different domains, you would have to use postMessage:
var invoice = window.open('https://yyy.com/receipt/receiptemplate.html');
invoice.postMessage(receiptdata, 'app.yyy');
and in your template
addEventListener('message', e => {
if(e.origin === 'http://app.yyy.com') {
window.receiptdata = e.data;
}
});

Related

How to get webpage in the same state after reloading in jquery and html?

I have a webpage which is in some state currently, that is, some divs are shown and some are hidden. I am using cookies to post data to Python CGI scripts. So now, when I click a button, I want it to send data to the python cgi file, and so, I want the button reload the page, so that, due to reloading the cookie can be updated and thus, the updated cookie can be sent to the python-cgi file. (Also, I am using iframe to display the python-cgi file inside my html webpage).
Now, the methods such as location.load() doesn't work as it reloads the webpage into the initial state where all my divs were hidden but I want it to be in the same state as before reloading where some divs were shown and some were hidden.
How can I achieve that? Please help me with that! Thanks!
The are two solutions I can think of to this problem:
JavaScript State-Saving Solution
Rewrite code as a REST API
Solution 1: JavaScript State-Saving Solution
You can write a JavaScript function for when the button is clicked. And another one for when the page is loaded. Using the localStorage API, you can then save the current state of the page.
function buttonClickedEvent() {
const div1Visible = !document.querySelector('#div1').hidden || true;
const div2Visible = !document.querySelector('#div2').hidden || true;
const userInput = document.querySelector('#user-input').value || '';
localStorage.setItem('page-state', JSON.stringify({
div1Visible,
div2Visible,
userInput
}));
window.location.href = '/path/on/server';
}
function setupPage() {
const state = localStorage.getItem('page-state');
let stateObj;
try {
stateObj = JSON.parse(state);
} catch (e) {
return;
}
document.querySelector('#div1').hidden = !stateObj.div1Visible;
document.querySelector('#div2').hidden = !stateObj.div2Visible;
document.querySelector('#user-input').value = stateObj.userInput;
}
With the following HTML:
<body onload='setupPage()'>
<button onClick='buttonClickedEvent()'>
Click Me
</button>
</body>
Solution 2: REST API (Ideal solution)
What I would recommend you do instead, is rewrite your server code such that it is a REST API, rather than using HTTP redirects. Then on the frontend, instead of changing the page, you use a library such as Axios to create HTTP requests:
axios.get('/path/on/server', (response) => {
if (response.status === 200) { // HTTP success
// Code to run upon successful response
}
});
Please let me know if there is anything you would like me to clarify.

Using Xrm.WebApi method in Web Resource opened in a new window

I have opened an HTML web resource in a new window using:
Xrm.Navigation.openWebResource(webResource, windowOptions, data);
This is an HTML web resource and it is loading the ClientObject in the head
<script type="text/javascript" src="../../../ClientGlobalContext.js.aspx" ></script>
then I have some JavaScript that is trying to retrieve a Contact
var contactId = "8553DA63-11C9-E711-A824-000D3AE0CB84";
var promise = Xrm.WebApi.retrieveRecord("contact", contactId, "$select=contactid,firstname,lastname");`
but this is failing. I've step-traced into the Xrm.WebApi method and found the error is when it attempts to resolve "contact" to a Set Name
Code from Global.ashx
getEntitySetName: function(logicalName) {
Mscrm.Utilities.addTelemetryLog("Xrm.Utility.getEntitySetName");
var $v_0 = window.ENTITY_SET_NAMES || window.top.ENTITY_SET_NAMES;
if (IsNull(this.$5H_1) && !isNullOrEmptyString($v_0))
this.$5H_1 = JSON.parse($v_0);
return this.$5H_1[logicalName.toLowerCase()]
},
For some reason the window.ENTITY_SET_NAMES object is null so an error (null reference) occurs
I've tried embedding my web resource into a CRM page and the code works correctly. The issue seems to be when the web resource is launched via Xrm.Navigation.openWebResource
Has anyone tried to use Xrm.WebApi in the context of an web resource opened with Xrm.Navigation.openWebResource? or does anyone know if there are additional steps required to retrieve data?
Update
ENTITY_SET_NAMES is initialised in main.aspx.
I tried embedding my custom Web Resource directly into a new Main Form section and the retrieveRecord method works.
It appears this is a problem only when running the Web Resource from a new page via Xrm.Navigation.openWebResource
Update 2 - Response to Aron
I tried using window.parent as suggested below
var contactId = "8553DA63-11C9-E711-A824-000D3AE0CB84";
var promise = parent.Xrm.WebApi.retrieveRecord("contact", contactId, "$select=contactid,firstname,lastname");`
and for good measure also tried window.parent.top
var contactId = "8553DA63-11C9-E711-A824-000D3AE0CB84";
var promise = parent.top.Xrm.WebApi.retrieveRecord("contact", contactId, "$select=contactid,firstname,lastname");`
but both resulted in the same error
Sounds like a product bug within ClientGlobalContext.js.aspx, as this should give you whole context to work with.
Probably you can utilize window.opener.Xrm in this scenario, since it worked for window.opener.Xrm.Page.getAttribute it should also work for Xrm.WebApi.
You can try to access variable from opener window like this:
window["ENTITY_SET_NAMES"] = window["ENTITY_SET_NAMES"] || window.opener.top.ENTITY_SET_NAMES;
My blog :)
To get this working I have implemented a hacky work-around.
I've been debugging the Xrm.WebApi method and it is failing on a line where it attempts to take the entityname and resolve it to the setname (plural). It does this by comparing the value passed into the retrieveRecord method and comparing it to a global variable ENTITY_SET_NAMES
In my example, it is trying to resolve contact to contacts
This variable is unfortunately not present and Xrm.WebApi throws an error
My work-around is to check for this variable, and if it is not present then create it! ENTITY_SET_NAMES is a JSON-parsable string which contains the logical name and set name for each entity.
window["ENTITY_SET_NAMES"] = window["ENTITY_SET_NAMES"] || JSON.stringify({
"account" : "accounts",
"contact" : "contacts"
});
Executing this line before any calls to Xrm.WebApi methods appears to work and I now get results
Here's the complete snippet:
window["ENTITY_SET_NAMES"] = window["ENTITY_SET_NAMES"] || JSON.stringify({
"account" : "accounts",
"contact" : "contacts"
});
var contactId = "8553DA63-11C9-E711-A824-000D3AE0CB84";
Xrm.WebApi.retrieveRecord(
"contact",
contactId,
"$select=contactid,firstname,lastname"
).then(
function success(result) {
console.log(result.firstname);
// perform operations on record retrieval
},
function (error) {
console.log(error.message);
// handle error conditions
}
);
As per this article, when referencing the main form from a Web Resource we have to reference the parent window. Though, it only references Xrm.Page and Xrm.Utility, it should also work with Xrm.WebApi...
An HTML web resource added to a form can’t use global objects defined by the JavaScript library loaded in the form. An HTML web resource may interact with the Xrm.Page or Xrm.Utility objects within the form by using parent.Xrm.Page or parent.Xrm.Utility, but global objects defined by form scripts won’t be accessible using the parent.
Please try parent.Xrm.WebApi.retrieveRecord("contact", contactId, "$select=contactid,firstname,lastname");
This article also demonstrates parent.Xrm.WebApi
If you are going to use bound actions & functions you'll also need to add a similar variable to map entities to their primary id fields.
window["ENTITY_PRIMARY_KEYS"] = ['{"account":"accountid", "contact":"contactid"}'];
Try using opener.Xrm to access the Xrm object in a standalone webresource opened from a form as described here in the parent.Xrm section: https://learn.microsoft.com/en-us/power-platform/important-changes-coming#some-client-apis-are-deprecated
"If the getContentWindow method doesn't work, you can use parent.Xrm to get to the Xrm object inside an HTML web resource. If the HTML web resource is opened in a new window, then you should use opener.Xrm instead."
var contactId = "8553DA63-11C9-E711-A824-000D3AE0CB84";
var promise = opener.Xrm.WebApi.retrieveRecord("contact", contactId, "$select=contactid,firstname,lastname");

Javascript file for multiple html pages

I am beginner in Javascript. I am currentlyworking on a Phonegap app with it. I am stuck in between as I have 4 html pages for signup process, and I have to pass all the html pages input value to single js file as in final all data must be POSTed to server URL and also I have read on many sites that they have recommended using same js file for all the pages of your site to speed up the site. So I have two problems to solve. I searched on many sites but could not find the accurate answer.
I need to pass 4 html page's input value to single js file.
I have to make single js file for both sign-in and sign-up.
My codes for JS page is:
var firstName="";
var lastName="";
var email="";
var password="";
var retypePassword="";
var gender="";
var DOB="";
var institute="";
var course="";
var branch="";
var semester="";
var teachers = [];
function signUpStarting() {
alert(firstName + " "+lastName+" "+email+" "+password+" "+retypePassword+" "+gender+" "+DOB+" "+institute+" "+course+" "+branch+" "+semester+" "+teachers.join(","));
}
function signUp1() {
firstName[0] = $("#first_name").val().trim();
firstName[1] = $("#last_name").val().trim();
email = $("#email").val().trim();
password = $("#password").val();
retypePassword = $("#retype_password").val();
alert(firstName + " "+lastName+" "+email+" "+password+" "+retypePassword);
}
function signUp2() {
gender = $('#gender').find(":selected").text();
DOB = $('#DOB').val();
alert(gender+" "+DOB);
}
function signUp3() {
institute = $('#institute').find(":selected").text();
course = $('#course').find(":selected").text();
branch = $('#branch').find(":selected").text();
semester = $('#semester').find(":selected").text();
alert(institute+" "+course+" "+branch+" "+semester);
}
function signUp4() {
$(":checkbox" ).map(function() {
if($(this).is(':checked')){
teachers.push($('label[for="' + this.id + '"]').text());
}
});
signUpStarting();
}
In html pages I am calling JS functions for each pages:
On first page:
<a onclick="signUp1()" href="register-two.html">continue</a>
On second page:
<a onclick="signUp2()" href="register-three.html">continue</a>
On third page:
<a onclick="signUp3()" href="register-four.html">continue</a>
On fourth page:
<a onclick="signUp4()">continue</a>
On each transaction from one page to next I have set alert in JS, and I am getting alert with accurate values also. But after clicking the continue button from fourth page of html, I transferred the code to main signup function. I tried to see alert in signUpStarting() function but there I am getting response of just fourth page values and other values are showing nothing as the variables are null.
I am not getting how to save variable values for always without using localStorage or cookies and POSTing all data to server.And I think this would have been easier if I would know to code for all html pages for my site to single JS file.
Please help me !
I am not getting how to save variable values for always without using localStorage or cookies and POSTing all data to server.And I think this would have been easier if I would know to code for all html pages for my site to single JS file.
This is exactly right. You cannot store data in memory between page loads in a web browser environment because all javascript variables are naturally destroyed when the browser navigates away from the page to a new page (even if they use the same javascript on both pages). Thus, you have to save it somewhere with more permanence: localStorage, cookies, or on the server via POST or GET.
What I would recommend is scrapping the four different html pages and simply using one html page that changes dynamically as the user fills in data. This way the browser will not eliminate data before you are ready to POST it to the server.

Accessing headers of a google spreadsheet using HTTP GET and Google App Script

I'm trying to return the header row of a Google spreadsheet using doGet() in a Google App Script that's running as a WebApp. I'm using a HTML form to send the GET request to the WebApp and it's all working except I don't know how to return the headers to my javascript. I'll post my code:
HTML:
<form id="getForm" method="get" action="My URL for WebApp">
<label for="sheetGetID">SheetID</label>
<input type="text" name="sheetGetID" id="sheetGetID" value="">
<button class="ui-btn" onclick='submitGET()'>Submit</button>
</form>
Javascript:
function submitGET() {
var headers = $("getForm").submit();
alert(headers);
}
Google App Script:
function doGet(e) {
//Trying To: Get headers from sheetID and then return to app, then have correct labels for the inputs, then use POST to post.
var ss = SpreadsheetApp.openById(ScriptProperties.getProperty('active'));
var sheet = ss.getSheetByName(e.parameter["sheetGetID"]);
//Return the first 3 cells, A1:C1,
var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];
return ContentService.createTextOutput(JSON.stringify(headers))
.setMimeType(ContentService.MimeType.JSON);
}
I'm getting a JSON object returned but it's just a text output. My question is how would/could I get the JSON returned and stored as the headers variable?
The return of doGet method must be an HTML.
Build another html page and use the call HtmlService.createTemplateFromFile('newPag.html').evaluate()
Inside your page use the tags and put your server side code manipulating the json object. This way you will create a good look and feel and a good maintanable code.
I got this to work a while ago, I forgot to post the answer just in case anyone else needed it.
You need to output it as a JSON object like the API demo. You also need to append "?prefix=?" to the url when you're doing a $.getJSON() call. The prefix part is to tell the JQuery that it is a JSON object you're receiving.
If anyone has troubles with this just comment and this and I'll post all the code I used.
So on your client end, I'm using JQuery Mobile, I'm not sure how to do it without it, you would do something like:
sheetID = $("#sheetGetID").val();
$.getJSON("https://script.google.com/macros/s/YOUR_KEY_GOES_HERE/exec?prefix=?",
{ sheetGetID: sheetID},
function(results) {
var fields = results.split(",");
//Do something with fields
}
);
}
Where #sheetGetID is the textbox where the user can enter the sheet id for headers.
Note the ?prefix=? appended to the URL, that part is for JQuery to know it's receiving JSON. That part is necessary. The URL is your deployed WebApp.
On the Google App Script side, ie Server side, you'd have something like:
function doGet(request) {
var ss = SpreadsheetApp.openById(ScriptProperties.getProperty('active'));
var sheet = ss.getSheetByName(request.parameter["sheetGetID"]);
//Return the first 3 cells, A1:C1,
var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];
var result = headers.join();
var content = request.parameters.prefix + '(' +JSON.stringify(result) + ')';
return ContentService.createTextOutput(content)
.setMimeType(ContentService.MimeType.JSON);
}
If you have any questions on how the spreadsheet part works theres plenty of documentation on Google's API's. doGet() is called when you use the $.getJSON(), the return from the G.A.S. needs to be JSON. Most of this is covered in the documentation Google has, some of it I found watching Google Developers Live on youtube. If you are trying to do more stuff I highly recommend checking those sources out.
If you have any more questions about what's being called or parameters you can find it easily enough on Google.

ExtJS: Multiple JsonStores from one AJAX call?

I have an ExtJS based application. When editing an object, an ExtJS window appears with a number of tabs. Three of these tabs have Ext GridPanels, each showing a different type of data. Currently each GridPanel has it's own JsonStore, meaning four total AJAX requests to the server -- one for the javascript to create the window, and one for each of the JsonStores. Is there any way all three JsonStores could read from one AJAX call? I can easily combine all the JSON data, each one currently has a different root property.
Edit: This is Ext 2.2, not Ext 3.
The javascript object created from the JSON response is available in yourStore.reader.jsonData when the store's load event is fired. For example:
yourStore.on('load', function(firstStore) {
var data = firstStore.reader.jsonData;
otherStore.loadData(data);
thirdStore.loadData(data);
}
EDIT:
To clarify, each store would need a separate root property (which you are already doing) so they'd each get the data intended.
{
"firstRoot": [...],
"secondRoot": [...],
"thirdRoot": [...]
}
You could get the JSON directly with an AjaxRequest, and then pass it to the loadData() method of each JSONStore.
You may be able to do this using Ext.Direct, where you can make multiple requests during a single connection.
Maybe HTTP caching can help you out. Combine your json data, make sure your JsonStores are using GET, and watch Firebug to be sure the 2nd and 3rd requests are not going to the server. You may need to set a far-future expires header in that json response, which may be no good if you expect that data to change often.
Another fantastic way is to use Ext.Data.Connection() as shown below :
var conn = new Ext.data.Connection();
conn.request({
url: '/myserver/allInOneAjaxCall',
method: 'POST',
params: {
// if you wish too
},
success: function(responseObj) {
var json = Ext.decode(responseObj.responseText);
yourStore1.loadData(json.dataForStore1);
yourStore2.loadData(json.dataForStore2);
},
failure: function(responseObj) {
var message = Ext.decode(responseObj.responseText).message;
alert(message);
}
});
It worked for me.

Categories