Reputation: 115
I'm converting a script of mine to be an add on. One of the needs is to configure a template, so I have programmed a sidebar that launchs a field picker. As the sidebar does not have enough room for the picker, I have to launch it from a modal dialog that I create from the sidebar, by calling this code in the server side:
var html = HtmlService.createHtmlOutputFromFile('TemplatePicker.html')
.setWidth(600).setHeight(425);
SpreadsheetApp.getUi().showModalDialog(html, 'Select the file with the template');
My problem is that once the user picks the file, when I have the id of the chosen file, I'm not able to pass that id to the sidebar. I tried invoking someJSFunctionOfSidebar(id)
and parent.someJSFunctionOfSidebar(id)
, but it didn't work, so I finally ended passing the value to the server side and reloading the sidebar from there, but it's very slow and the resulting effect is ugly.
My question is:
Is there a way to pass a value at client level from a modal dialog created with SpreadsheetApp.getUi().showModalDialog
to its parent? Perhaps it's not really its parent and that's the reason for it not working.
Upvotes: 4
Views: 2033
Reputation: 50453
Sidebar and modal dialog(siblings) are not able to communicate despite having same origin.
It is possible to get a reference to the sidebar html from modal dialog through the ancestor parent window.top
, even though the parent is cross origin. From there, it is possible to
window.postMessage()
to communicate with each otherWithout a reference to each other, it is still possible to communicate with each other through
window.top
):addOn.html[Sidebar]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Addon</title>
<style>
#spinner {
display: none;
background-color: tomato;
position: absolute;
top: 1%;
width: 100%;
justify-items: center;
}
</style>
</head>
<body>
<div id="spinner"><p>Loading modal dialog...</p></div>
<div id="output"></div>
<script charset="utf-8">
google.script.run.withSuccessHandler(spinner).testModal();
function spinner(e) {
document.getElementById('spinner').style.display = e || 'flex';
}
(async () => {
//After modal dialog has finished, receiver will be resolved
let receiver = new Promise((res, rej) => {
window.modalDone = res;
});
var message = await receiver;
document.querySelector('#output').innerHTML = message;
//Do what you want here
})();
</script>
</body>
</html>
modalAddOn.html[Modal dialog/picker]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title></title>
</head>
<body>
Modal Dialog
<script>
(function findSideBar(limit) {
let f = window.top.frames;
for (let i = 0; i < limit; ++i) {
try {
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found ');
alert('Removing loadbar and closing self');
var sidebar = f[i][0][0];
sidebar.spinner('none'); //Remove sidebar spinner
sidebar.modalDone('Modal says Hi'); //Modal has finished
google.script.host.close();
}
} catch (e) {
console.error(e);
continue;
}
}
})(10);
</script>
</body>
</html>
code.gs[Server]
function testModal() {
SpreadsheetApp.getUi().showModelessDialog(
HtmlService.createHtmlOutputFromFile('modalAddOn')
.setHeight(500)
.setWidth(300),
' '
);
}
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('Sidebar')
.addItem('Show Add-On', 'showSidebar')
.addToUi();
}
function showSidebar() {
SpreadsheetApp.getUi().showSidebar(
HtmlService.createTemplateFromFile('addOn.html').evaluate()
);
}
Upvotes: 8
Reputation: 45720
Perhaps it's not really its parent and that's the reason for it not working.
Right - there isn't actually a DOM parent / child relationship in play here. Both the sidebar and the modal dialog were launched from server-side scripts, and are independent. They can both communicate with the server, though, so you can use a store-and-forward technique to get the result from your picker to the sidebar.
Basic idea:
google.script.run
.Have a look at How to poll a Google Doc from an add-on for the basic idea of a poller.
Upvotes: 10