I'm using the new Google Sites and I want to display a text box in it, but my goal is to show a text that is written in a cell in a Google sheet. So I can just edit the content in this sheet and it also change in the site.
I've written this code, but it is not working. Someone know what I can do?
function doGet() {
return HtmlService.createHtmlOutputFromFile('txtSheetToSite');
}
function getText(row, col) {
const ss = SpreadsheetApp.openById("here_goes_the_sheet_id");
const sheet = ss.getSheetByName("Orientacoes");
var row = row;
var col = col;
var value = sheet.getRange(row, col).getValue();
return value;
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
function copyTxt(2,1){
var text = google.script.run.getText();
console.log(text);
document.getElementById("titulo").innerText = text;
}
copyTxt();
</script>
</head>
<body>
<div id="titulo"> </div>
</body>
</html>
The code is using google.script.run the wrong way. From https://developers.google.com/apps-script/guides/html/reference/run
Return
void — this method is asynchronous and does not return directly; however, the server-side function can can return a value to the client as a parameter passed to a success handler; also, return types are subject to the same restrictions as parameter types, except that a form element is not a legal return type
Replace
function copyTxt(2,1){
var text = google.script.run.getText();
console.log(text);
document.getElementById("titulo").innerText = text;
}
by
function copyTxt(){
google.script.run.withSuccessHandler(function(text){
console.log(text);
document.getElementById("titulo").innerText = text;
})
.getText(2,1);
}
Related
I created a web form using Google Apps Script, where form visitors would see result.html after data submission. However, the data may be submitted multiple times if visitors reload the result.html by pressing F5, Ctrl + R, ignoring the alert of resubmission. The same concern has already been posted here, and I tried implementing one of the solutions for that, but in vain.
I have now four files in the same project of Google Apps Script:
index.html that produces the form
JavaScript.html that defines functions used in index.html
result.html that is presented after the form submission
code.gs that shows the form by doGet(), and processes the submitted data and presents result.html by doPost(). include() defined in this file enables to input JavaScript.html into index.html
The solution I have tried is adding the following JavaScript code result.html. I also add that to JavaScript.html so that the code is to be executed in index.html, too.
<script>
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
However, the resubmission still occurs when I reload the result.html even after I added that code to both result.html and index.html. What am I missing?
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<!-- <?!= include("css"); ?> -->
</head>
<body onload="addOptions()"> <!--Execute addOptions function immediately after a page has been loaded-->
<form class="" action="<?!= getScriptUrl(); ?>" method="post" onSubmit="document.getElementById('submit').disabled=true;">
<div>
<h1 id="Question">
Choose either cheesecake or chocolate cake.
</h1>
<select id="dropdownList" name="cake" class="form-control">
</select>
</div>
<div class="form-submit">
<input type="submit" name="" value="Submit">
</div>
</form>
</body>
<?!= include('JavaScript') ?>
</html>
JavaScript.html
<script>
function addOptions() {
/*This will call server-side Apps Script function getAvailableExps and if it is successful,
it will pass the return value to function addListValues which will add options to the drop down menu*/
google.script.run
.withFailureHandler(onFailure)
.withSuccessHandler(addListValues)
.getAvailableExps();
}
function addListValues(values) {
//Add options to drop down menu using the values of parameter 'values'.
for (var i = 0; i < values.length; i++) {
var option = document.createElement("option");
option.text = values[i][0];
option.value = values[i][0];
var select = document.getElementById("dropdownList");
select.appendChild(option);
}
}
function onFailure(err) {
alert('Error: ' + err.message);
}
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
result.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<base />
<title>Thank you for your order!</title>
<!-- <?!= include('css'); ?> -->
</head>
<script>
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>
<body>
<p>
Don't forget what you've ordered!
</p>
</body>
</html>
code.gs
var sheetID = "............................................";
var inventory_sheet = "Inventory";
function doGet(){
return HtmlService.createTemplateFromFile("index").evaluate();
}
function include(filename){
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getScriptUrl() {
var url = ScriptApp.getService().getUrl();
Logger.log(url);
return url;
}
function doPost(e){
var ss = SpreadsheetApp.openById(sheetID);
var sh = ss.getSheets()[0];
sh.appendRow([String(e.parameters.cake)]);
//update Inventory
var inventory = ss.getSheetByName(inventory_sheet);
var row = inventory.createTextFinder(e.parameters.cake).findNext().getRow();
var range = inventory.getRange(row, 2);
var data = range.getValue();
range.setValue(parseInt(data - 1))
return HtmlService.createTemplateFromFile("result").evaluate();
}
function getAvailableExps(){
var inventory = SpreadsheetApp.openById(sheetID).getSheetByName(inventory_sheet);
var data = inventory.getRange(2, 1, 2, 2).getValues();
var filtered = data.filter(arr => arr[1] > 0 || arr[1] != ''); //remove exp to array if quantity is 0 or empty
return filtered;
}
In your situation, how about checking the submit using PropertiesService? When your script is modified, it becomes as follows.
Modified script:
In this modification, 2 functions of doGet and doPost of code.gs are modified.
doGet
function doGet() {
PropertiesService.getScriptProperties().setProperty("key", "sample");
return HtmlService.createTemplateFromFile("index").evaluate();
}
doPost
function doPost(e) {
var p = PropertiesService.getScriptProperties();
if (p.getProperty("key") == "sample") {
var ss = SpreadsheetApp.openById(sheetID);
var sh = ss.getSheets()[0];
sh.appendRow([String(e.parameters.cake)]);
//update Inventory
var inventory = ss.getSheetByName(inventory_sheet);
var row = inventory.createTextFinder(e.parameters.cake).findNext().getRow();
var range = inventory.getRange(row, 2);
var data = range.getValue();
range.setValue(parseInt(data - 1))
p.deleteProperty("key");
}
return HtmlService.createTemplateFromFile("result").evaluate();
}
When you access to your Web Apps, sample is stored by setProperty("key", "sample") in doGet(). And, when the HTML form is submitted, the PropertiesService is checked in doPost(e). When sample is existing, the data is put, and the PropertiesService is cleared. By this, even when the submitted page is reopened, the PropertiesService is not existing. By this, the resubmitted can be avoided.
Reference:
Properties Service
I know that the <script> element can have function show(shown, hidden) on it. but with the 2 pages ({document.getElementById(shown).style.display='block'; document.getElementById(hidden).style.display='none'; return false;) in that, I can't figure out how to make that page count more. Any help?
P.S. I am open to almost anything. I can't guarantee your answers will help, but I might be able to figure it out using your suggestions.
I have tried more things on the function show(shown, hidden, hidden, hidden) but that does not help.
I am stuck. I have researched anything I could find. I can't figure it out.
Please help me.
My specific code I want suggestions on is this:
<script>
function show(shown, hidden) {
document.getElementById(shown).style.display='block';
document.getElementById(hidden).style.display='none';
return false;
}
</script>
with some <div>s.
I know this is probably not helping you figure out how to help me, but I need to know. (I hate full-on JavaScript!)
<!doctype html>
<html lang="en">
<head>
<title>Multi but Single Page</title>
<meta charset="utf-8">
<style>
.templates {
display: none;
}
</style>
<script>
// we save all templates in an global Variable
var templateStack = [];
// https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
function getParameterByName(name, url) {
url = url || window.location.href;
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
window.addEventListener('load', function (e) {
// get all hidden template elements
var templates = document.getElementsByClassName('templates');
for (var i = 0, v; v = templates[i]; i++) {
// each Child Node is a new Page
for (var j = 0, x; x = v.childNodes[j]; j++) {
// at least if it's an element
if (x.nodeType === Node.ELEMENT_NODE) {
templateStack.push(x);
}
}
}
// uri support ?page=1 loads Page 2 and ?page=0 loads Page 1, default is 0
var pageIndex = getParameterByName('page') || '0';
// so we can test it with a Browser by just insert 'loadPage(1)'
loadPage(pageIndex);
});
function loadPage(index) {
// only valid indexes
if (index >= templateStack.length || index < 0) {
document.body.innerText = '404 Page not found';
return;
}
// clean everything in our page
document.body.innerHTML = '';
// append our fetched Page out of our Global Variable
document.body.appendChild(templateStack[index]);
}
</script>
</head>
<body>
<div class="templates">
<div>
<h3>Page 1</h3>
<p>
Welcome to Page 1
</p>
Load Page 2
</div>
<div>
<h1>Page 2</h1>
<p>
Our Page 2
</p>
Back to Page 1
</div>
</div>
</body>
</html>
I understand that you can use it with 2 pages but when you want to make more pages like 4-5 pages?
First you need an clear function (it will hide all the pages)
In the clear function get the body in dom and get all the childrens then make a foreach loop hiding all of them
Second you need an show function which will use the page as an parameter like "show('page1');" it will first call the clear function and then show the page1
I'm trying to get the result from an existing Javascript function on a local html page, by using CefSharp in a Windows Form application.
The html page source is:
<!DOCTYPE html>
<html>
<body>
<p id="demo">A Paragraph.</p>
<script>
function myFunction() {
document.getElementById("demo").innerHTML = true;
return 1 + 1;
}
</script>
</body>
</html>
My C# code is:
private void ChromeBrowser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
if (!args.IsLoading)
{
string result = RunScriptParamAsync("myFunction").ToString();
}
}
public string RunScriptParamAsync(string scriptName)
{
string script = "";
script = scriptName;
//script = string.Format("(function myFunction() {{ document.getElementById('demo').innerHTML = \"{0}\"; return 1 + 1; }})();", scriptName);
chromeBrowser.EvaluateScriptAsync(script).ContinueWith(x =>
{
var response = x.Result;
if (response.Success && response.Result != null)
{
dynamic result = response.Result;
return ((int)result).ToString();
}
else
{
return string.Empty;
}
});
return string.Empty;
}
If I use the commented line
//script = string.Format("(function myFunction() {{ document.getElementById('demo').innerHTML = \"{0}\"; return 1 + 1; }})();", scriptName);
then I'm getting the correct result (2), but the idea is to use a Javascript function already existing on a web page.
A breakpoint inside the function reveals this:
I've also tried
chromeBrowser.GetMainFrame().EvaluateScriptAsync(script)
but with same results.
Any ideas?
You are getting exactly what you are asking for, a reference to the function.You need to append (); to actually execute the function.
//Will return a IJavascriptCallback, which is effectively a function pointer, which is what you have asked for
await browser.EvaluateScriptAsync("myFunction");
//To execute the function you must append ();
await browser.EvaluateScriptAsync("myFunction();")
There are two steps on my code. First step a user fills some fields and then data is submitted by ajax to server. Ajax returns a HTML select input that user must choose a value. This is the second step.
The problem is, when I try to get the value of select in javascript, it shows me null.
The code I use to get select value works in normal situation. But when select is retrieved by ajax, this problem occurs.
Code to get select value
var e = document.getElementById("ordernum");
var num = e.options[e.selectedIndex].value;
Here's an example HTML file showing how to dynamically generate a select element and get the value back from it on its onchange event via event.target.value:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>
const GameDifficulty = {
EASY:1,
MEDIUM:2,
HARD:3,
INSANE:4
};
function init(event) {
console.log(event);
var body = document.getElementById('body');
var select = document.createElement('select');
select.id = 'selDifficulty';
for (var diff in GameDifficulty) {
var option = document.createElement('option');
option.value = GameDifficulty[diff];
option.innerHTML = diff;
select.appendChild(option);
}
select.onchange = test;
body.appendChild(select);
console.log(body);
}
function test(event) {
console.log(event.target.value);
}
window.onload = init;
</script>
</head>
<body id="body">
</body>
</html>
I need some help because my callback function, parseMovie() is only being called once! Despite being in a for loop which iterates it twice. I am using a free Rottentomatoes API
The output only returns one ID, and not two ID's!
And runs parseMovie() only once and returns the movie ID with the last movie.
Does anyone have a fix for this script running problem?
HTML CODE
<!doctype html>
<html class="no-js">
<head>
<title>Movies</title>
<link rel="stylesheet" href="css/main.css">
<script src="js/main.js"></script>
</head>
<body>
<form name="input">
<p> Actor/Actress Name: <input type="text" name="fullName"> </p>
<p> Movie 1 <input type="text" name="movie"> </p>
<p> Movie 2 <input type="text" name="movie"> </p>
<p><input type="button" value="Search movies" onclick="getMovies()"></p>
<p><textarea name="output" readonly> </textarea> </p>
</form>
</body>
</html>
JAVASCRIPT
//api key
var APIKEY = "qf54ubt95fea9n7jytr5xh6h";
var movieID = new Array();
var actor = new Array();
var actorName = "Jennifer Lawrence";
var movieTitle;
var output;
function callScript(call) {
var script = document.createElement('script');
script.setAttribute("src", call);
document.body.appendChild(script);
}
function getMovies() {
for (var x=0; x<2; x++) {
movieTitle = document.getElementsByName('movie')[x].value;
movieTitle= cleanMovieTitle(movieTitle);
var movieURL = "http://api.rottentomatoes.com/api/public/v1.0/movies.json?q=";
callScript(movieURL + movieTitle + "&page_limit=10&page=1&apikey=" + APIKEY + "&callback=parseMovie");
}
}
function cleanMovieTitle(movie) {
movie = movie.trim();
movie = movie.replace(/ /g, "+");
return movie;
}
function parseMovie(data) {
var titleData = data.movies;
for (var t=0; t<titleData.length; t++) {
movieID[movieID.length] = titleData[t].id;
aCast = titleData[t].abridged_cast;
sample = [];
for (var person = 0; person < aCast.length; person++) {
sample[sample.length] = aCast[person].name;
}
actor[actor.length] = sample;
}
for (var arry = 0; arry < actor.length; arry++) {
if (actor[arry].indexOf(actorName) >= 0) {
output = movieID[arry];
break;
} else {
alert("spelling error of some sort! Error 404");
}
}
document.input.output.value = output;
}
Your statement var titleData = data.movies; is wrong, because the data returned by the API contains an array of movies.
You have to iterate through data.movies to get the data for the other movies (and not only the first one).
See the raw JS code and JSON data returned by the API: api.rottentomatoes.com
Three things that strike me as odd that might be causing the problem.
Using for…in for an array is considered bad practice, especially when there's a native forEach method and a polyfill for ie8-
cleanMovieTitle isn't doing anything because it doesn't return a value. If you were passing it an array or object, it would pass by reference and it would be altered, but that is considered bad practice for the exact reason that it's not working. You're passing a value, the function modifies that value within the function's scope, then does nothing with it. You need to return the string and set movieTitle = cleanMovieTitle(movieTitle); So maybe the API isn't returning for one of the titles because it hasn't been cleaned. See Passing by Reference or by Value.
The callback may be is getting called again before it finishes running. Not sure on this one, but you could check by flooding the loop with console.logs and seeing whether it's the case.
Edit
So I just ran your script on this page and I'm getting an error on document.input.output.value = output; As expected, parseMovies runs twice when I remove this line. What's document.input?