So, i've been experimenting with de google apps script lately. So far so good, but i ran into a problem that's drivin me out: I have a button in a spreadsheet that calls a sidebar menu with a function in scripts
macros.gs
function sbCases() {
var Form = HtmlService.createTemplateFromFile("Cases");
var ShowForm = Form.evaluate();
ShowForm.setTitle("ASS-CAD - Cases manager system").setHeight(400).setWidth(1000);
SpreadsheetApp.getUi().showSidebar(ShowForm);
the html file I call with this function works just fine, but I'd like to call a second form, also trough an html file to manage the spreadsheet data. So i've added this function to the .gs file (and started a new html file):
function NovoCasoMSE(){
var Form = HtmlService.createTemplateFromFile("NewCase");
var ShowForm = Form.evaluate();
ShowForm.setTitle("New Case").setHeight(400).setWidth(1000);
SpreadsheetApp.getUi().showModalDialog(ShowForm, "New Case");
}
but when I try to call it from a button in the first html file, nothing happens at clicking the button (checked the log and the function the button should call isn't being executed.
Follow the code (the html is full of stuff, like the buttons and everything)("btn" is the ID for a button working on the html file):
<script>
document.getElementById("btn").addEventListener("click", NewCase);
function NewCase(){
google.script.run.NewCase()
}
</script>
I'm learning c in college but have very little experience in javascript ou google script, so I'm pretty sure I've done something really wrong. Thanks for any help in advance. :)
You can try something like this:
Run showTSidebar to get things rolling and then click the button.
ag1.gs:
function loadForm() {
var html='<form><input type="text" name="name1"/><input type="button" value="Click" onClick="process(this.parentNode);" /></form>';
return html;
}
function showTSidebar() {
SpreadsheetApp.getUi().showSidebar(HtmlService.createTemplateFromFile('ah4').evaluate());
}
function processForm(obj) {
SpreadsheetApp.getUi().alert('name1: ' + obj.name1);
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
ah4.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('sbresrc') ?>
</head>
<body>
<div id="form"></div>
<input type="button" value="Load Form" onClick="loadForm();" />
<?!= include('ah6') ?>
</body>
</html>
ah6.html:
<script>
function loadForm() {
google.script.run
.withSuccessHandler(function(html){
$('#form').html(html);
$('#form').css('display','block');
})
.loadForm();
}
function process(obj) {
google.script.run.processForm(obj);
}
</script>
sbresrc.html:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
animation:
Related
In my apps script addon there is a lot of buttons and I want to pass the button ID as variable in the function executed. I have this execFunction in code.gs to avoid google.script.run duplicates, works well without the btnID parameter, but it doesnt let me pass an argument to the final function. fa seems not valid.
What could be the right path to have the possibility of make if/else depending on the clicked button id?
index.html
<button id='freezerButton' class="btn btn-primary" onclick="execFunction('myFunction', this.id)">FREEZER</button>
<script>
function execFunction(functionName, btnID) {
google.script.run[functionName](btnID);
}
</script>
code.gs
function myFunction(btnID) {
if (btnID == freezerButton) {
Logger.log('From freezerButton')
}
}
Thanks!
Replace freezerButton by 'freezerButton', or before the if statement, declare freezerButton assigning the appropriate value, i.e.
const freezerButton = 'freezerButton';
Instead of passing this.id on the onclick attribute, pass this.
Example:
code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('index')
}
function myFunction(id){
console.log(id)
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button id="myButton" onclick="execute('myFunction',this)">Click me!</button>
<script>
function execute(name,btn){
google.script.run[name](btn.id)
}
</script>
</body>
</html>
Related
How to get the onclick calling object?
onClick to get the ID of the clicked button
enter image description herewe've tried to write a script for a web app to a specific spreadsheet
the code is 2 files one in js
function doGet() {
return HtmlService.createHtmlOutputFromFile("page");
}
//function userClicked(name) {
//var url = "https://docs.google.com/spreadsheets/d/1T3AX_YBC8703g6N7sKot41tXUh6XN4zpcBF2V-_7iJ8/edit#gid=0";
//var ss = SpreadsheetApp.openByUrl(url);
//var ws = ss.getSheetByName("Data");
//ws.appendRow([name])
//Logger.log(name + "Clicked Run Button");
//}
function userClicked() {
Logger.log("Someone Clicked the button");
}
and the other in html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1>Welcome</h1>
<button id="btn">RUN !!</button>
<script>
function doSomething() {
google.script.run.code.userClicked();
}
document.getElementById("btn").addEventListener("click", doSomething());
</script>
</body>
</html>
we can't get the desired action when run button is clicked
we don't know if it's the declaration of functions or summoning them
please help guide to rectify the error
this is yje project link for further analysis
https://script.google.com/d/1daP7bLBlL46av4Wc6-Pr9-z9lg6JyMY44FUtfA08fnKRKLeMuCTxH3LY/edit?usp=sharing
According to the Google Script client side API documentation, the path to call the App Script function is google.script.run.yourFunction() so in your html file. Also you are invoking the function straight away and passing the result of it to addEventListener(), instead of passing the function itself. The script should be this:
function doSomething() {
google.script.run.userClicked();
}
document.getElementById("btn").addEventListener("click", doSomething);
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<textarea id="textetrad" rows="5"></textarea>
<textarea id="translated-text" rows="5"></textarea>
<button id="run-translation" onclick="runTranslation();">Translate</button>
<script>
function runTranslation() {
var data = document.getElementById("textetrad").value;
var trad = LanguageApp.translate(data, 'en', 'es');
document.getElementById("translated-text").value = trad;
}
</script>
</body>
</html>
Hello, I am creating a bar lateral and I have a problem. I would like to create a textarea where we insert what we want and it is translated in another textarea. I have tried different methods like LanguageApp.translate(data , 'en', 'es') but I can't get this function to work in the HTML code.
So already is what I want possible without using an API?
If yes, should I do it only in the HTML code or should I make the HMTL code and the .gs communicate?
And then how do I transmit the translation from the gs code to the HTML?
I tried and made it work on apps script using LanguageApp
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
</head>
<script type="text/javascript">
function runTranslation() {
google.script.run.withSuccessHandler(onSuccess).translate(document.getElementById('textetrad').value);
}
function onSuccess(data) {
document.getElementById('translated-text').value = data;
}
</script>
<body>
<textarea id="textetrad" rows="5"></textarea>
<textarea id="translated-text" rows="5"></textarea>
<button id="run-translation" onclick="runTranslation();">Translate</button>
</body>
</html>
Code.gs:
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
function translate(textValue){
// automatically identify the language, then translate to spanish
return LanguageApp.translate(textValue, '', 'es');
}
Process:
upon onclick, execute runTranslation.
when translate function (in Code.gs) runs with no issue, proceed with onSuccess
passing the translated data, assign it to the other element
Output:
Reference:
HTML Service: Communicate with Server Functions
I have the following code:
code.gs:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('My Menu')
.addItem('Test', 'showTestForm')
.addToUi();
}
function showTestForm() {
var html = HtmlService.createHtmlOutputFromFile('TestForm');
SpreadsheetApp.getUi().showModalDialog(html, 'TEST');
}
function Test(formObject){
Logger.log("TEST")
var a = new Error( "Allready present "+ formObject);
a.error_code = 99;
Logger.log(JSON.stringify(a));
throw a;
}
TestForm.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<base target="_top">
<script>
function onFailure(error) {
var keys = Object.keys(error);
alert(JSON.stringify(keys));
alert(JSON.stringify(error.message));
alert(JSON.stringify(error.error_code));
}
function onSuccess() {
alert("Success");
}
</script>
</head>
<body>
<input type="submit" value="Save" onclick="google.script.run.withFailureHandler(onFailure).withSuccessHandler(onSuccess).Test('1')" />
<input type="button" value="Close" onclick="google.script.host.close()" />
</body>
</html>
When I open TestForm from menu and press "Save" I've got following log from Logger:
[18-12-24 23:08:24:765 PST] TEST
[18-12-24 23:08:24:766 PST] {"message":"Allready present 1","error_code":99}
So I see, that error object have properties 'message' and 'error_code'. But in browser I've got following alerts:
["name"]
"Error: Allready present 1"
undefined
So I see, that recived error object has only one empty (i've checked) property "name". But if I but refer to the property "message, I've got string like in original object (but not the same). And it looks like that object haven't poperty "error_code".
What's the matter?
I thought you might like a complete working example as I know this stuff can be quite frustrating.
This a simple example templated HTML file that can be used as a dialog or a webapp. All it does is create a Google Doc file with todays date in the header and footer of each page and it puts the file into the same directory as the spreadsheet which contains the script. That's it. I use the Chrome Browser. I don't even care if my scripts won't run on another browser.
Here's the HTML: (FileName:'docwithdate.html')
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('resources') ?>
<?!= include('css') ?>
</head>
<body>
<?!= include('form') ?>
<?!= include('script') ?>
</body>
</html>
The Resources: (FileName: 'resources.html')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
The CSS: (FileName: 'css.html')
<style>
body {background-color:#ffffff;}
input[type="button"]{padding:0 0 2px 0;}
</style>
The Form: (FileName: form.html) This is probably push the templating idea a little far.
<form id="myForm" onsubmit="event.preventDefault();processForm(this);" >
<input type="text" id="txt1" name="filename" />
<input id="btn" type="submit" value="Submit" />
</form>
The Javascript: [FileName: 'script.html')
<script>
function createFile(){
var name=document.getElementById('filename').value;
google.script.run
.withSuccessHandler(function(rObj){
var html='<br />Go To File:' + rObj.filename + '';
$(html).appendTo("body");
})
.createTemplatedGoogleDoc(name);
}
function getInputObject(obj) {//I'm probably doing something wrong here. But this is what I had to do to get the object with the properties that I desired. So if you have another way. Go for it.
var rObj={};
for(var i=0;i<Object.keys(obj).length;i++){
if(obj[i].type=="text"){
rObj[obj[i].name]=obj[i].value;
}
console.log('Object.keys(rObj): %s',Object.keys(rObj).join(', '));
}
return rObj;
}
function processForm(obj){
var fObj=getInputObject(obj);
var name=fObj.filename;
google.script.run
.withSuccessHandler(function(rObj){
document.getElementById("btn").disabled=true;
var html='<br />Go To File:' + rObj.filename + '';
$(html).appendTo("body");
})
.createTemplatedGoogleDoc(name);
}
console.log('My Code');
</script>
The Google Script: (FileName: Code.gs)
function onOpen(){
SpreadsheetApp.getUi().createMenu('My Menu')
.addItem("Open Templated Google Doc", 'showMyDialog')
.addToUi()
}
function createTemplatedGoogleDoc(name){
Logger.log(name);
var doc=DocumentApp.create(name);//Creates a google doc
var fldrs=DriveApp.getFileById(SpreadsheetApp.getActive().getId()).getParents();
while(fldrs.hasNext()){
var fldr=fldrs.next();
if(fldr.getName()=="Create Templated Google Doc App"){
var folder=fldr;
}
}
Drive.Files.update({"parents": [{"id": folder.getId()}]}, doc.getId());//puts doc file into same directory as the spreadsheet that contains the script
doc.addHeader().appendParagraph(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "E MMM dd, yyyy"));
doc.addFooter().appendParagraph(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "E MMM dd, yyyy"));
//doc.getBody().getChild(0).removeFromParent();
doc.saveAndClose()
var rObj={url:doc.getUrl(),filename:doc.getName()}
return rObj;
}
function showMyDialog(){
var ui=HtmlService.createTemplateFromFile('docwithdate').evaluate();
SpreadsheetApp.getUi().showModelessDialog(ui, 'My Doc with Date');
}
function doGet(){//if you want a web app this is helpful
return HtmlService.createTemplateFromFile('docwithdate').evaluate();
}
function include(filename){//this is the include that the template uses
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
It's a pretty simple script. I hope it helps you get a start.
In accordance with the proposal of #TheMaster it is necessary to do this:
code.gs
function Test(formObject){
var a = new Error( JSON.stringify({msg:"Allready present "+ formObject,code:99}));
throw a;
}
TestForm.html
// removing "Error: " from message string to get our json back
var json = error.message.replace("Error: ",'')
var msg = JSON.parse(json).msg;
var code = JSON.parse(json).code;
That is, we put json into the attribute message of the Error object, and then, by cutting our json, we parse it and get the necessary values.
This is not exactly the answer to the question, but a good way to solve the problem.
I'm using QUnit for unit testing js and jquery.
My HTML looks like this:
<!DOCTYPE html>
<html>
<head>
<title>QUnit Test Suite</title>
<script src="../lib/jquery.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.16.0.css" type="text/css" media="screen">
<script type="text/javascript" src="http://code.jquery.com/qunit/qunit-1.16.0.js"></script>
<!--This is where I may have to add startPage.html--->
<script src="../login.js"></script>
<script src="../test/myTests.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>
Currently, I'm adding login.js as shown and I'm getting references correctly to objects defined in login.js.
However, functions in login.js contains references to some dom elements defined in startPage.html which is located elsewhere.
So, if I say $('#login-btn'), it is throwing an error. Is there any way to fix this?
Can I
(a) refer to startPage.html to my qunit page given above?
(b) refer to or load startPage.html in the file where I'm running tests (myTests.js):
QUnit.test( "a test", function( assert ) {
assert.equal( 1, "1", "String '1' and number 1 have the same value" );//works
assert.equal( login.abc, "abc", "Abc" );//works with attributes
assert.equal(($("#userid").val()),'', 'Userid field is present');//fails
assert.equal( login.ValidUserId(), true, "ValidUserId" );//fails with functions
});
Does QUnit provide any method to load Html/php files so they'll be defined prior to testing. Like 'fixtures' in jasmine?
EDIT: Please also tell what to do in case I have startPage.php
There are a couple of ways you can do this. The simplest is just to use the built-in QUnit "fixtures" element. In your QUnit HTML file, simply add any HTML you want in the div with the id of qunit-fixture. Any HTML you put in there will be reset to what it was on load before each test (automatically).
<html>
...
<body>
<div id='qunit'></div>
<div id='qunit-fixture'>
<!-- everything in here is reset before each test -->
<form>
<input id='userid' type='text'>
<input id='login-btn' type='submit'>
</form>
</div>
</body>
</html>
Note that the HTML in the fixture doesn't really have to match what you have in production, but obviously you can do that. Really, you should just be adding the minimal necessary HTML so that you can minimize any side effects on your tests.
The second option is to actually pull in the HTML from that login page and delay the start of the QUnit tests until the HTML loading is complete:
<html>
<head>
...
<script type="text/javascript" src="http://code.jquery.com/qunit/qunit-1.16.0.js"></script>
<script>
// tell QUnit you're not ready to start right away...
QUnit.config.autostart = false;
$.ajax({
url: '/path/to/startPage.html',
dataType: 'html',
success: function(html) {
// find specific elements you want...
var elem = $(html).find(...);
$('#qunit-fixture').append(elem);
QUnit.start(); // ...tell QUnit you're ready to go
}
});
</script>
...
</head>
...
</html>
Another way to do this without using jquery is as follows
QUnit.config.autostart = false;
window.onload = function() {
var xhr = new XMLHttpRequest();
if (xhr) {
xhr.onloadend = function () {
if(xhr.status == 200) {
var txt = xhr.responseText;
var start = txt.indexOf('<body>')+6;
var end = txt.indexOf('</body>');;
var body_text = txt.substring(start, end);
var qunit_fixture_body = document.getElementById('qunit-fixture');
qunit_fixture_body.innerHTML = body_text;
}
QUnit.start();
}
xhr.open("GET", "index.html");
xhr.send();
} else {
QUnit.start(); //If getting the html file from server fails run tests and fail anyway
}
}