UI to HTML, conversion will not write a date to the sheet - html

I am trying to replicate a question posed by Pieter Jaspers regarding a conversion of a form from UIApp to HTML. The original question is:
Original question by Pieter Jaspars answered by Sandy Good
If I replicate the code exactly I get the correct result, but when I try to recreate my inline form and amalgamate the two I am not getting a result. The inline form question is here:
HTML inline form formatting question answered by Mogsdad
The code I have so far is in the requisite number of parts, a form and two .gs sheets. I have checked for spelling mistakes and I have tried editing out each line to see what and where I get, but with my limited experience I am drawing a blank. The only minor success is if I run the function InsertInSS() from within the code editor. This posts the word "undefined" in the correct cell in the correct spreadsheet, but not the date as I am trying to do!
Form:
<!-- Use a templated HTML printing scriptlet to import common stylesheet. -->
<?!= HtmlService.createHtmlOutputFromFile('Stylesheet').getContent(); ?>
<html>
<body>
<div>
<!-- Page header title & 'completion warning -->
<span class="grey"><b>ProReactive Log Form v3.0.74</b></span>
<h4>Complete ALL fields to ensure the form is processed successfully</h4>
<div class="block">
<!-- First input box created -->
<div class="inline form-group">
<label form="date">Date</label>
<input type="date" id="date" style="width: 125px;">
</div>
</div>
</div>
<!-- End of fields for completion, finish form with submission button -->
<button class="share" onclick="runGoogleScript()">submit</button>
</body>
</html>
<script>
function onSuccess(argReturnValue){
alert('was successful ' +argReturnValue);
//ResetFields on Screen
Document.getElementById("date").value = "";
}
function runGoogleScript() {
logger.log('v3.0.74 ran!');
var inputValue = document.getElementById("date").value;
google.script.run.withSuccessHandler(onSuccess)
.InsertInSS(inputValue);
};
</script>
The Code.gs:
function doGet(e) {
return HtmlService.createTemplateFromFile('myForm')
.evaluate()
.setTitle('ProReactiveLog')
.setSandboxMode(HtmlService.SandboxMode.NATIVE);
};
and the seperate.gs:
function InsertInSS(argPassedInName) {
var ssKey = '1cZuEMDrIyzIFm4lGCT20s-Wpv27o0hbbZAsxD1WJFL8';
var SS = SpreadsheetApp.openById(ssKey);
var Sheet = SS.getSheetByName('LOG');
Sheet.getRange(Sheet.getLastRow()+1,1,1).setValue(argPassedInName);
}
Any ideas where I am going wrong? All help as ever, greatly appreciated.

An Apps script HTML App, will not allow a date object to be passed to the server.
google.script.run Parameter Documentation
The documentation states:
Requests fail if you attempt to pass a Date, Function, DOM element besides a form, or other prohibited type, including prohibited types inside objects or arrays.
You will need to pass the date as a string. Convert the date to a string in client side code, and convert it back to a date, if need be, in the server code. However, even if you set the value in the spreadsheet as a string, it may get coerced back into a date without you needing to do anything. Especially if you have that column defined as a date in the spreadsheet.

Related

using a variable to populate the "mailto:" value using "form action"

Newbie question no.2, sorry in advance!
I have somehow managed to create a form with various selection boxes, one of which is the email that the form should send the email to (using mailto:). I've managed to get the value of the email field stored as a variable ("emailtouse"), and now I am trying to use the variable in the "mailto:" code but it's not having it, I either get blank or the variable name itself when I attempt the process.
Thanks
Ian
***variable setting within script in header***
var emailtouse = "mailto:"+emailgoto[value]
***form action***
<form action='+emailtouse+'?
cc=u16#myleague.co.uk&subject=Match%20Postponement/%20Cancellation%20Request" method="post"
enctype="text/plain">
Even if your variable is updated, the "action" is not updated after the variable changes, so it contains the original value, calculated upon rendering the page.
Please see the following CodePen example on how to update the form action before submit:
<form
id="form1"
onsubmit="return updateAction(this)"
action="javascript:;"
method="post">
<button type="submit">Do it!</button>
</form>
... and the JS to update the form action, and to test that it really worked:
let emailtouse = "testemail#somewhere.com";
function updateAction(element) {
element.action =
emailtouse +
"&cc=u16#myleague.co.uk&subject=Match%20Postponement/%20Cancellation%20Request";
checkIfItReallyWorks();
return false; // change to true to submit!!!
}
function checkIfItReallyWorks() {
let form = document.getElementById("form1");
alert(form.action);
}
The above code on CodePen: https://codepen.io/cjkpl/pen/vYxPJQd

Pull data to a html page from Google Sheets based on user input

I have a Google spreadsheet with several rows of data in four columns whose headings are
Employee ID |
Name |
Age |
Designation |
I want to create a web App (html page with form elements) in which if I put in the Employee ID value (which is unique) in a text field and click Submit, the other three details (viz. Name, Age, Designation) of only that particular Employee are displayed in a table below in that same page.
I was not able to see any examples where select parts of the sheet are returned as a table based on user input value.
Would appreciate help.
While I will not be able to code your web app, you can start by researching client-server communication in HTML. The google.script.run API is the most basic call, you can start on that.
You may want to research on scriptlets as well. Basically this allows you to execute Apps Script code in your HTML using the tags <? and ?>.
The templated HTML documentation provides some examples that you can expand to suit your need.
You may also want to study some examples here.
Try something like this:
Note I haven't tested this so some tweaking may be required.
HTML:
<html>
<head>
<base target="_top">
</head>
<body>
<div id="msg"></div><!-- You can add css to hide these and then turn them on with js as required -->
<div id="found"></div>
<form>
<input type="text" name="id" placeholder="Enter Employee ID" /><br />
<input type="button" value="Search" onClick="processForm(this.parentNode);" />
</form>
<script>
function processForm(obj) {
google.script.run
.withSuccessHandler((obj)=>{
if(!obj.msg) {
document.getElementById('found').innerHTML=`EmployeeId: ${obj.id} Name: ${obj.n} Age: ${obj.a} Designation: ${obj.d}`;
}else{
document.getElementById('found').innerHTML=`Message: ${obj.msg}`;
}
})
.search(obj);
}
console.log("My Code");
</script>
</body>
</html>
GS:
function mysearch(obj) {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const [hA, ...data]=sh.getDataRange().getValues();
let idx={};
let found=false;
hA.forEach((h,i)=>{idx[h]=i;});
for(var i=0;i<data.length;i++) {
if(data[i][idx['EmployeeID']]==obj.id) {
Logger.log({id:obj.id,n:data[i][idx['Name']],a:data[i][idx['Age']],d:data[i][idx['Designation']]});
found=true;
break;
}
}
if(found){
return {id:obj.id,n:data[i][idx['Name']],a:data[i][idx['Age']],d:data[i][idx['Designation']]};
} else {
return {msg:"Not found"}
}
}
function launchMyNewDialog() {
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutputFromFile('ah1'),'test');
}

How to resize user prompt so a box appears rather than a row?

I'm using the ui.prompt() to get user input for the rest of my apps script to work. However, I'd like it if the space where the user enters their response were larger, so should they have to type a considerate amount (4-5 sentences), the entire text is visible rather than having to mouse over to the beginning/end of the input.
I'm not sure if there's a function which allows me to set a custom size (height and width, or variable such as a "drag the corner" of the text window) for the input section.
var ui = SpreadsheetApp.getUi();
var bodyresponse = ui.prompt(
"The default email template is: " +
"\n\nIf you have any questions regarding your order, please email us directly by replying." +
"\n\nIf you would like to update your contact information, billing/shipping address, " +
"or have an adjustment to make on the Purchase Order attached to this email, " +
"please reach out to us within 7 days of receiving this Purchase Order." +
"\n\nThank you from Area Code 407!" +
"\n\nWould you like to include an accompanying note? If so, include it below: \n\n\n\n ",
ui.ButtonSet.YES_NO);
enter image description here
Unfortunately, the prompt functionality will not let you do this. You can, however, achieve what you want by making a Dialog box. Then, using client-to-server communication you can pass the information back to your server-side script.
This is not a perfect example but the general idea of what needs to be done.
Code.gs
var htmlOutput = HtmlService
.createHtmlFromFile('Dialog')
.setWidth(250)
.setHeight(300);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'My Title');
function dialogData(userNote){
//Do something with userNote...
}
Dialog.html
<body>
Hello, world!
<p> The default email template is: </p>
<p>If you have any questions regarding your order, please email us directly by replying. If you would like to update your contact information, billing/shipping address, or have an adjustment to make on the Purchase Order attached to this email, please
reach out to us within 7 days of receiving this Purchase Order.</p>
<p>Thank you from Area Code 407! </p>
<p>Would you like to include an accompanying note? If so, include it below: </p>
<textarea id="userNote" rows="4" cols="80"></textarea>
<input type="button" value="Submit" onclick="returnData()" />
<script>
function returnData() {
var note = document.getElementById("userNote").value
console.log(note);
google.script.run.onSuccessHandler(closeMe).dialogData(); //This calls a script in your main Code.gs serverside.
}
function closeMe() {
google.script.host.close();
}
</script>
</body>

How to acquire information from form in javascript?

Hey guys i was wondering how to acquire information from form in javascript and i found the method with object forms, but i doesnt want to work(returns undefined), because object is undefined. Do you know what i am doing wrong in that, could you attach some explanations? I know what it is possible to do it by getElement function,but i would like to understand why this solution doesn't work.
Regards!
<script type="text/javascript">
function cos(sth){
var par = document.getElementById("para1");
par.style.color = 'blue';
par.style.fontSize="30px";
par.style.fontFamily="Impact,Charcoal,sans-serif";
}
function getFormValue(){
document.write(5+6);
var doc = document.forms["myForm"]["email"].value;
// OR
var doc = document.forms[0].elements["name"];
document.write(doc);
}
</script>
</head>
<body>
<p id ="para1">JavaScript Exercises - w3resource</p>
<div>
<button onclick="cos();">Style</button>
</div>
<form name="myForm">
<input type="text" name="email"/>
<button onclick="getFormValue()">
</form>
</body>
</html>
document.write(5+6);
var doc = document.forms["myForm"]["email"].value;
Since the page has loaded, the document is in a closed state.
Calling document.write implicitly calls document.open which creates a new document.
You then write 11 to this document.
Next you try to get the form element. It doesn't exist in this new document.
Then you try to get the email field from it. Since you don't have a form, you get an error.
If you fix that:
var doc = document.forms["myForm"]["email"].value;
// OR
var doc = document.forms[0].elements["name"];
document.write(doc);
You read the value of the email field and assign it to doc.
Then you overwrite it with elements["name"].
There is no form control called name, so you get undefined.
var doc = document.forms["myForm"]["email"].value;
document.write(doc);
… works fine. You just need to remove the junk you put around it to break it.

Prevent HTML Page Refresh

At this stage I'm mostly used to backend Javascript and server side Java, so my HTML is not as savvy as it needs to be.
I've built several applications that require user input with Apps script, but I was using the now deprecated UI service, as I'm not a designer and this provided an easy way to design simple pages to pass data back and forth. With the UI service having been deprecated for some time, I'm begging the arduous task of migrating these services to the HTML service, and I'm noticing some difference in behavior.
For example, when submitting a form, the entire page refreshes to a blank page, and I can't seem to prevent that. The UI service would remain static for information re-entry, and I can't find a working method to get the HTML service to either stop refreshing or reload the form.
Simple code to reproduce my issue:
function doGet() {
return HtmlService.createHtmlOutputFromFile('test')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function logValues(value){
Logger.log('Something worked....');
}
With the index file being:
<form>
<input type="submit" value="Book Meeting" onclick="google.script.run
.logValues()">
</form>
Some things I've tried:
1) Adding a callback to the 'doGet' function, to attempt to get the page to load again.
2) Adding a whole new function to try and call a NEW HTML page.
The issue here is my poor understanding of the HTML service, but is there a simple way for me to just clear the form for re-submission, or alternatively just reload the page? None of the other questions I've found on SO adequately answer this question in a way I can understand.
Since you're technically submitting your form by clicking the submit button, then that creates the page refresh. You need to cancel the submit event with the preventDefault function, which "Cancels the event if it is cancelable, without stopping further propagation of the event."
See the docs here: https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault
So maybe you can try something along these lines (straight from the docs):
function stopDefAction(evt) {
evt.preventDefault();
}
document.getElementById('my-checkbox').addEventListener('click', stopDefAction, false);
Another option is to remove the form/input elements and simply use a button element instead, which doesn't trigger a page refresh on click.
It's an interesting ride switching old UI services across, I just did that with one of my applications and it has really improved the readability of the code. I posted a copy of a basic version of what I was doing in another question
Once you get your head around it all it becomes a lot simpler. This is a really basic example of using multiple HTML files similar to your example using the HTMLService when submitting forms (you can pass in parameters instead)
Code.gs
function doGet() {
return HtmlService.createTemplateFromFile('Main')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.NATIVE);
}
function onLogin(form) {
if (form.username == "fuzzyjulz") {
var template = HtmlService.createTemplateFromFile('Response');
//Setup any variables that should be used in the page
template.firstName = "Fuzzy";
template.username = form.username;
return template.evaluate()
.setSandboxMode(HtmlService.SandboxMode.NATIVE)
.getContent();
} else {
throw "You could not be found in the database please try again.";
}
}
function include(filename) {
return HtmlService.createTemplateFromFile(filename)
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.getContent();
}
Main.html
<?!= include('CSS'); ?>
<script>
function loadPage(htmlOut) {
var div = document.getElementById('content');
div.innerHTML = htmlOut;
document.getElementById('errors').innerHTML = "";
}
function onFailure(error) {
var errors = document.getElementById('errors');
errors.innerHTML = error.message;
}
</script>
<div id="errors"></div>
<div id="content">
<?!= include('Login'); ?>
</div>
CSS.html
<style>
p b {
width: 100px;
display: inline-block;
}
</style>
Login.html
<script>
function onLoginFailure(error) {
var loginBtn = document.getElementById('loginBtn');
loginBtn.disabled = false;
loginBtn.value = 'Login';
onFailure(error);
}
</script>
<div class="loginPanel">
<form>
<p>
<b>Username: </b>
<input type="text" name="username"/>
</p>
<input type="button" id="loginBtn" value="Login" onclick="this.disabled = true; this.value = 'Loading...';google.script.run
.withSuccessHandler(loadPage)
.withFailureHandler(onLoginFailure)
.onLogin(this.parentNode)"/>
</form>
</div>
Response.html
<div class="text">
Hi <?= firstName ?>,<br/>
Thanks for logging in as <?= username ?>
</div>