I'm new to the scripting, but from what I have learned from this website, I have put together a check book balancesheet in google sheets.
I have a function "AddCheck" attached to the button on one of the cell. What it does is - opens a dialog box where I enter check details, like Date, Invoice number, amount and vendor's name. Then I click the submit button and Google sheets creates a row and adds those values.
My Question is how do I add a button next to the submit button that will allow me to add the New check details without leaving the dialog box. So that it will add the values to the cells and will clear the dialog box for another Entry.
This is my AddCheck function
function AddCheck() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('3:3').activate();
spreadsheet.getActiveSheet().insertRowsBefore(spreadsheet.getActiveRange().getRow(), 1);
spreadsheet.getActiveRange().offset(0, 0, 1, spreadsheet.getActiveRange().getNumColumns()).activate();
spreadsheet.getRange('A3').activate();
spreadsheet.getCurrentCell().setFormula('=A4+1');
spreadsheet.getRange('G3').activate();
spreadsheet.getCurrentCell().setValue('Pending');
spreadsheet.getRange('B3').activate();
fncOpenMyDialog()
};
This is my HTML dialog file
<!DOCTYPE html>
<html>
<body>
<form>
<label for="Date">Date :</label>
<input type='date' name='Date' id="Date" required="required"/>
<br>
<label for="Invoice">Invoice</label>
<input type='text' name='Invoice' id="Invoice" required="required"/>
<label for="Amount">Amount</label>
<input type='text' name='Amount' id="Amount" required="required"/>
<label for="Company">Company</label>
<select name="Class" id="vendor-selector" autofocus="autofocus" autocorrect="off" autocomplete="off">
<script>
(function () {
google.script.run.withSuccessHandler(
function (selectList) {
var select = document.getElementById("vendor-selector");
for( var i=0; i<selectList.length; i++ ) {
var option = document.createElement("option");
option.text = selectList[i][0];
select.add(option);
}
}
).getSelectList();
}());
</script>
</select>
<input type="submit" value="Submit" onclick="myFunction(this.parentNode)">
</form>
<p id="CompanyName"></p>
<script>
function myFunction(obj) {
var x = document.getElementById("vendor-selector").value;
document.getElementById("CompanyName").innerHTML = x;
google.script.run
.withSuccessHandler(() => google.script.host.close())
.functionToRunOnFormSubmit(obj);
}
</script>
</body>
</html>
This Function calls the Dialog
function fncOpenMyDialog() {
//Open a dialog
var htmlDlg = HtmlService.createHtmlOutputFromFile('CheckDetails')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(200)
.setHeight(250);
SpreadsheetApp.getUi()
.showModalDialog(htmlDlg, 'Check Details');
};
function functionToRunOnFormSubmit(fromInputForm) {
Logger.log(fromInputForm);
var ss = SpreadsheetApp.getActive();
ss.getSheetByName("Checks").getRange(3,3).setValues([[fromInputForm.Class]]);
ss.getSheetByName("Checks").getRange(3,2).setValues([[fromInputForm.Date]]);
ss.getSheetByName("Checks").getRange(3,4).setValues([[fromInputForm.Invoice]]);
ss.getSheetByName("Checks").getRange(3,6).setValues([[fromInputForm.Amount]]);
};
and this Function gets the List of vendors from Sheet2
function getSelectList()
{
var sheet = SpreadsheetApp.openById("141mlnxJBjepKxYCGXHFhz5IIEVnp6T2DDsb_uRgnZzY").getSheetByName('Sheet2');
var lastRow = sheet.getLastRow();
var myRange = sheet.getRange("A2:A" + lastRow);
var data = myRange.getValues();
Logger.log("Data = " + data);
return data;
};
function doGet()
{
return HtmlService.createHtmlOutputFromFile('CheckDetails');
}
Thank you for your help.
In order to implement this "Submit & Continue" feature, you only need to change the front-end code so that:
There is a new button.
Upon clicking the new button, the same back-end (GAS) code gets executed.
The dialog does not get closed after the GAS code execution, but the input elements are reset.
Code modifications
Add an id to the form tag so that it may be easily selected.
<form id="CheckDetailsForm">
Create a "Submit & Continue" button in your HTML. Assign an id to it so that it may be easily selected.
<input type="submit" value="Submit and continue" id="SubmitAndContinueInput" />
Within the script tag, add an event listener to the newly created button.
document.getElementById("SubmitAndContinueInput").addEventListener("click", myFunctionContinue);
Add the handler function for the newly created button's onclick event. This function will be very similar as the one you are using but: It will use the preventDefault to avoid the form being incorrectly sent and will clear the form data upon submission.
function clearForm() {
document.getElementById('Date').value = "";
document.getElementById('Invoice').value = "";
document.getElementById('Amount').value = "";
}
function myFunctionContinue(e) {
e.preventDefault();
var obj = document.getElementById("CheckDetailsForm");
var x = document.getElementById("vendor-selector").value;
document.getElementById("CompanyName").innerHTML = x;
google.script.run
.withSuccessHandler(clearForm)
.functionToRunOnFormSubmit(obj);
}
Example dialog:
Edit
Handling "submit and continue" new rows:
Remove the row-insertion functionality from AddCheck(). We will handle this logic afterwards:
function AddCheck() {
fncOpenMyDialog();
}
Modify the functionToRunOnFormSubmit() function so that it handles the row-insertion logic. I have also cleaned up the code a little bit. It would look like the following:
function functionToRunOnFormSubmit(fromInputForm) {
Logger.log(fromInputForm);
var sheet = SpreadsheetApp.getActive().getSheetByName("Checks");
sheet.insertRowBefore(3);
sheet.getRange("A3").setFormula("=A4+1");
sheet.getRange("B3").setValue(fromInputForm.Date);
sheet.getRange("C3").setValue(fromInputForm.Class);
sheet.getRange("D3").setValue(fromInputForm.Invoice);
sheet.getRange("F3").setValue(fromInputForm.Amount);
sheet.getRange("G3").setValue("Pending");
}
Full code
CheckDetails.html
<!DOCTYPE html>
<html>
<body>
<form id="CheckDetailsForm">
<label for="Date">Date :</label>
<input type='date' name='Date' id="Date" required="required"/>
<br>
<label for="Invoice">Invoice</label>
<input type='text' name='Invoice' id="Invoice" required="required"/>
<label for="Amount">Amount</label>
<input type='text' name='Amount' id="Amount" required="required"/>
<label for="Company">Company</label>
<select name="Class" id="vendor-selector" autofocus="autofocus" autocorrect="off" autocomplete="off">
<script>
(function () {
google.script.run.withSuccessHandler(
function (selectList) {
var select = document.getElementById("vendor-selector");
for( var i=0; i<selectList.length; i++ ) {
var option = document.createElement("option");
option.text = selectList[i][0];
select.add(option);
}
}
).getSelectList();
}());
</script>
</select>
<input type="submit" value="Submit" id="SubmitInput" />
<input type="submit" value="Submit and continue" id="SubmitAndContinueInput" />
</form>
<p id="CompanyName"></p>
<script>
document.getElementById("SubmitInput").addEventListener("click", myFunction);
document.getElementById("SubmitAndContinueInput").addEventListener("click", myFunctionContinue);
function clearForm() {
document.getElementById('Date').value = "";
document.getElementById('Invoice').value = "";
document.getElementById('Amount').value = "";
}
function myFunction(e) {
e.preventDefault();
var obj = document.getElementById("CheckDetailsForm");
var x = document.getElementById("vendor-selector").value;
document.getElementById("CompanyName").innerHTML = x;
google.script.run
.withSuccessHandler(() => google.script.host.close())
.functionToRunOnFormSubmit(obj);
}
function myFunctionContinue(e) {
e.preventDefault();
var obj = document.getElementById("CheckDetailsForm");
var x = document.getElementById("vendor-selector").value;
document.getElementById("CompanyName").innerHTML = x;
google.script.run
.withSuccessHandler(clearForm)
.functionToRunOnFormSubmit(obj);
}
</script>
</body>
</html>
Code.gs
function AddCheck() {
fncOpenMyDialog()
}
function fncOpenMyDialog() {
//Open a dialog
var htmlDlg = HtmlService.createHtmlOutputFromFile('CheckDetails')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(200)
.setHeight(250);
SpreadsheetApp.getUi()
.showModalDialog(htmlDlg, 'Check Details');
}
function functionToRunOnFormSubmit(fromInputForm) {
Logger.log(fromInputForm);
var sheet = SpreadsheetApp.getActive().getSheetByName("Checks");
sheet.insertRowBefore(3);
sheet.getRange("A3").setFormula("=A4+1");
sheet.getRange("B3").setValue(fromInputForm.Date);
sheet.getRange("C3").setValue(fromInputForm.Class);
sheet.getRange("D3").setValue(fromInputForm.Invoice);
sheet.getRange("F3").setValue(fromInputForm.Amount);
sheet.getRange("G3").setValue("Pending");
}
function getSelectList() {
var sheet = SpreadsheetApp.openById("141mlnxJBjepKxYCGXHFhz5IIEVnp6T2DDsb_uRgnZzY").getSheetByName('Sheet2');
var lastRow = sheet.getLastRow();
var myRange = sheet.getRange("A2:A" + lastRow);
var data = myRange.getValues();
Logger.log("Data = " + data);
return data;
}
function doGet() {
return HtmlService.createHtmlOutputFromFile('CheckDetails');
}
Related
I need to publish individuals' exam results from a google sheet. I have found some code and created another HTML page which now gives me the required result properly if I click the SHOW link. The app script is :
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1L1Qu6QCaDucr4Jy5eOAnQkX-wpYjz6eevqAMzBc72iQ/edit#gid=0");
var sheet = ss.getSheetByName("Sheet1");
function doGet(e){
return search(e) ;
}
function doPost(e){
return search(e) ;
}
function search(e) {
var id = e.parameter.id;
var values = sheet
.getDataRange()
.getValues()
.filter(function(row) {
return row[0] == id;
});
var content = JSON.stringify(values);
return ContentService.createTextOutput(content).setMimeType(
ContentService.MimeType.TEXT
);
}
and the HTML is :
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input type="text" id="txtName"/>
SHOW
</body>
</html>
It gives the result in an array like [[ROLL, "Name", MARKS1, MARKS2, MARKS3...]]. Now I need to use these values at an HTML page like
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1>INSTITUTE NAME</h1>
Name <input type="text" id="name"/><br>
Roll <input type="text" id="roll"/><br>
Subject 1 <input type="text" id="sub1"/><br>
Subject 2 <input type="text" id="sub2"/><br>
Subject 3 <input type="text" id="sub3"/><br>
Subject 4 <input type="text" id="sub4"/><br>
Subject 5 <input type="text" id="sub5"/><br>
</body>
</html>
I mean like
What should I do?
You don't need two HTML, you can do the entire process with one HTML using google.script.run [1] to communicate your code.gs with the Index.html, and update it with the data from the spreadsheet.
Just create an Index.html file with this:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input type="text" id="txtName"/>
<button id="show">SHOW</button>
<h1>INSTITUTE NAME</h1>
Name <input type="text" id="name"/><br>
Roll <input type="text" id="roll"/><br>
Subject 1 <input type="text" id="sub1"/><br>
Subject 2 <input type="text" id="sub2"/><br>
Subject 3 <input type="text" id="sub3"/><br>
Subject 4 <input type="text" id="sub4"/><br>
Subject 5 <input type="text" id="sub5"/><br>
</body>
<script>
//When click on show button it will run search function
window.onload = function(e){
document.getElementById('show')
.addEventListener('click', search);
}
//Get the value for txtName input and run search function in code.gs
function search() {
var txtName = document.getElementById('txtName').value;
google.script.run.withSuccessHandler(fillInfo).withFailureHandler(function (e) { console.log(e) }).search(txtName);
}
//It will run when a success response comes from search function in code.gs and updates the input with the sheet info
function fillInfo(values) {
document.getElementById('name').value = values[1];
document.getElementById('roll').value = values[0];
document.getElementById('sub1').value = values[2];
document.getElementById('sub2').value = values[3];
document.getElementById('sub3').value = values[4];
document.getElementById('sub4').value = values[5];
document.getElementById('sub5').value = values[6];
}
</script>
</html>
code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('Index');
}
//Search for the id and return the array for that row
function search(id) {
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1L1Qu6QCaDucr4Jy5eOAnQkX-wpYjz6eevqAMzBc72iQ/edit#gid=0");
var sheet = ss.getSheetByName("Sheet1");
var values = sheet
.getDataRange()
.getValues()
.filter(function(row) {
return row[0] == id;
});
return values[0];
}
Deploy the application and you can access to it with the URL.
[1] https://developers.google.com/apps-script/guides/html/communication
I am assuming that the results array is like the one you told. I think the best way to handle these is creating a dom node and appending to a container element and wrapping that logic to a looping function. Here's my example:
const resultElement = (value) => {
let wrapper = document.createElement("div");
//ROLL
let rollElement = document.createElement("input");
rollElement.setAttribute("type","text");
rollElement.value = value[0];
wrapper.appendChild(rollElement);
//Name
let nameElement = document.createElement("input");
nameElement.setAttribute("type","text");
nameElement.value = value[1];
wrapper.appendChild(nameElement);
//Marks
for(let i = 2; i < value.length; i++) {
let markElement = document.createElement("input");
markElement.setAttribute("type","text");
markElement.value = value[i];
markElement.setAttribute("id", ("mark_"+i));
wrapper.appendChild(markElement);
}
return wrapper;
}
const handleAllValues = (values) => {
let container = document.getElementById("container");
for(let i = 0;i<values.length;i++) {
let el = resultElement(values[i]);
container.appendChild(el);
}
}
and you should have an element with id="container" in your html.
I have HTML code with some JS as follows:
<form>
Object: <input type="text" name="object">
<br>
brand: <input type="text" name="brand">
<br>
<br>
color: <input type="text" name="color">
<br>
<input type="submit" value="Submit"onclick="doTest()">
</form>
<h3>Results</h3>
formValues.object = <span id="object"></span><br>
formValues.brand = <span id="brand"></span><br>
formValues.color = <span id="color"></span><br>
<script id="jsbin-javascript">
var formValues = {};
function inputObj(formNR, defaultValues) {
var inputs = formNR.getElementsByTagName('input');
for ( var i = 0; i < inputs.length; i++) {
formValues[inputs[i].name] = defaultValues[i];
if(inputs[i].type === 'text') {
inputs[i].value = defaultValues[i];
document.getElementById(inputs[i].name).innerHTML = defaultValues[i];
}
inputs[i].addEventListener('keyup', function() {
formValues[this.name] = this.value;
document.getElementById(this.name).innerHTML = this.value;
}, false);
}
}
var defValues =['','',''];
inputObj(document.forms[0], defValues);
</script>
When the user inputs some text, this text becomes a variable. E.g there is a variable called "formValues.object". Then I want to take the value of this variable and write it onto a google sheet using the following code
function doTest() {
SpreadsheetApp.getActiveSheet().getRange('I2').setValue(" ");
}
The problem is that since the data I want to enter is a variable I do not know what I have to put between the .setValue brackets in order for the data the variable stores to appear in cell I2 of the google sheet when the submit button is pressed (I have already figured out how to link the submit button with the function).
If your value is an object and you want to put it into a single cell you will need to convert the variable to a string.
function doTest() {
SpreadsheetApp.getActiveSheet().getRange('I2').setValue(JSON.stringify(MyVariable));
}
I'm using a Google spreadsheet and want to create a sidebar menu with multiple check boxes and button. When selection 1 is selected or check box is clicked, and user presses Enter, the values from check boxes should display in the selected cell in the spreadsheet.
There is an input type of checkbox:
<form action="">
<input type="checkbox" name="myCategory" value="Yes">I like toast<br>
<input type="checkbox" name="myCategoryTwo" value="No">Don't like toast
</form>
gs Code
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Custom Menu')
.addItem('Show sidebar', 'showSidebar')
.addToUi();
};
gs Server code To Create Dialog
function showSidebar() {
Logger.log('showSidebar ran: ' );
var html = HtmlService.createTemplateFromFile('Dialog')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('My custom sidebar')
.setWidth(300);
SpreadsheetApp.getUi()
.showSidebar(html);
}
HTML
<div>
<form action="">
<input id="idYesChk" type="checkbox" name="myCategory" value="Yes">I like toast<br>
<input type="checkbox" name="myCategoryTwo" value="No">Don't like toast
</form>
<input type="text">
<input type='button' value="Update Spreadsheet" onclick='fncCallServerToDoSomething()'>
</div>
<script>
function fncCallServerToDoSomething() {
console.log('fncCallServerToDoSomething ran!');
var aReturnedValue = document.getElementById("idYesChk").value;
console.log("value of check box: " + aReturnedValue);
google.script.run
.withFailureHandler(onFailure)
.gsFunctionToRun(aReturnedValue);
//google.script.host.close();
};
function onFailure(error) {
console.log(error.message);
};
</script>
gs Code To write value to Spreadsheet
Puts Value Into Active Cell
function gsFunctionToRun(argValueRetrieved) {
Logger.log('argValueRetrieved: ' + argValueRetrieved);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = ss.getActiveSheet();
var activeCell = ss.getActiveCell().getA1Notation();
var theRange = theSheet.getRange(activeCell);
theRange.setValue(argValueRetrieved);
};
Puts Value into Range (cell) Designated
function gsFunctionToRun(argValueRetrieved) {
Logger.log('argValueRetrieved: ' + argValueRetrieved);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = ss.getActiveSheet();
var theRange = theSheet.getRange("B4");
theRange.setValue(argValueRetrieved);
};
Below is my code for dynamic generation of rows:
function addRow() {
var myName = document.getElementById("name");
var type = document.getElementById("type");
var table = document.getElementById("myTableData");
var rowCount = table.rows.length;
var row = table.insertRow(rowCount);
row.insertCell(0).innerHTML=myName.value;
row.insertCell(1).innerHTML=type.value;
row.insertCell(2).innerHTML= '<input type="button" value = "Delete" onClick="Javascript:deleteRow(this)">';
row.insertCell(3).innerHTML= ' Edit';
}
and below is my code for popup after clicking on edit link:
<div class="popup">
<p>Please edit your details here</p>
<div>
<label for="firstname" id="attr_Name">Attribute Name</label>
<input type="text" id="firstname" value="" />
</div>
<div>
<label for="lastname" id="attr_Type">Attribute Type</label>
<select id="type1" ><option >Text</option><option >Paragraph</option><option >Dropdown</option></select>
</div>
<input type="button" id="button1" value="Save" onclick="saveEditedValues()"/>
<a class="close" href="#close"></a>
</div>
Now I am using local storage to save my edited values but I am not getting how to reflect it in the dynamically generated rows. Below is code for Local storage:
function saveEditedValues(){
var myName = document.getElementById("firstname").value;
alert(myName);
var type = document.getElementById("type1").value;
alert(type);
localStorage.setItem("attributeName",myName.value);
localStorage.setItem("attributeType",type.value);
var namevar1=localStorage.getItem("attributeName");
var namevar2=localStorage.getItem("attributeType");
}
Please provide some help
In order to update the table, the save function will need to be able to locate the correct row, which means you will have to pass it something like the row number.
When adding the row, define the onclick event handler of the Edit link to pass rowCount
row.insertCell(3).innerHTML= ' Edit';
Add a hidden input to your popup div
<input type="hidden" id="editingRow" />
and have the Edit function populate that value:
function Edit(rowNum) {
...
document.getElementById("editingRow").value = rowNum;
...
}
Then the saveEditedValues function can locate the row in the table and update the values
function saveEditedValues(){
...
var rowNum = document.getElementById("editingRow").value;
var row = document.getElementById("myTableData").rows[rowNum];
row.cells[0].innerHTML = myName;
row.cells[1].innerHTML = type;
...
}
like so: jsFiddle
var myName = document.getElementById("firstname").value;
alert(myName);
var type = document.getElementById("type1").value;
alert(type);
myName and type are the correct values (strings). So
localStorage.setItem("attributeName",myName.value);
localStorage.setItem("attributeType",type.value);
is wrong, you have to use the plain variables like this:
localStorage.setItem("attributeName",myName);
localStorage.setItem("attributeType",type);
The crux of this problem is that assigning a variable to an html element is not working within a constructor function.
There must be a way around this right?
The most effective way I have found is to create a method within the constructor function that returns the element.
The problematic variable is "box".
I commented out the section at the start where I tried to make box a global variable, but the constructor couldn't find the box variable. That is the weirdest part to me.
Below is my sample code:
window.onload = function()
{
document.getElementById("sub_button").onclick = adder;
document.getElementById("scrap_it").onclick = remover;
}
//var box = document.getElementById("contact_list");
//refers to the select tag containing contact names as options
var Contacts = function()
{
this.box = function (){ return document.getElementById("contact_list");}
this.list = [];
this.contact_info = document.getElementById("contact_info");
this.find = function(personName){
var found = "missing";
for(var i = 0; i < this.list.length; i++)
{
if(this.list[i].personName == personName)
{
found = i;
}
}
return found;
}
this.addPerson = function(personName, phone)
{
if (this.find(personName) == "missing")
{
personName = personName;
contact =
{
personName: personName,
phone: phone
}
this.list.push(contact);
this.update();
}
else
{
alert("Sorry, this contact name is already in use. Please choose another.");
}
}
this.update = function()
{
this.box().innerHTML = "";
for (var i = 0; i <this.list.length; i++)
{
option_element = document.createElement("OPTION");
option_node = document.createTextNode(this.list[i].personName);
option_element.appendChild(option_node);
this.box().appendChild(option_element);
}
}
this.remove = function(name_to_delete)
{
var index_to_remove = name_to_delete;
this.list.splice(index_to_remove, 1);
this.update();
}
this.postInfo = function(contact_to_display)
{
var index_to_display = contact_to_display;
alert(this.list[index_to_display].personName);
alert(this.list[index_to_display].phone);
}
}
var myList = new Contacts();
function adder()
{
myList.addPerson(document.getElementById("contact_name").value, document.getElementById("contact_phone").value);
}
function remover()
{
myList.remove(myList.box().selectedIndex);
}
function showInfo()
{
myList.postInfo(myList.box().selectedIndex);
}
And the HTML:
<html>
<head>
<title>Address Book</title>
<script type="text/javascript" src="beta3.js"></script>
</head>
<body>
<form id="contact_form">
<label for="contact_name">Name: </label>
<input type="text" id="contact_name" /><br />
<label for="contact_phone">Phone: </label>
<input type="text" id="contact_phone" /><br />
<input type="button" name="submit" value="submit" id="sub_button" />
</form>
<br />
<div>
Delete
</div>
<br />
<div>
<select name="contact_list" id="contact_list" size="10" multiple="multiple" style="width: 450px">
</select>
</div>
<div>
<textarea id="contact_info">
</textarea>
</div>
</body>
</html>
try something like this
var box;
window.onload = function()
{
document.getElementById("sub_button").onclick = adder;
document.getElementById("scrap_it").onclick = remover;
//refers to the select tag containing contact names as options
box = document.getElementById("contact_list");
}
Your code is not working because your script is executed before our element is render in dom so your box variable get nothing.