I have a google sheet like this
I Need to make a google appscript webapp like below with the above sheet.
only the price field need to be editable.
When it is edited and save is clicked, it should be updated in the sheet.
How i can do this in a easy way?
PS:I know html and css part well. I just need the script part.
Thanks in Advance
A script to get your data
function lfunko() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet Name");
const [h,...vs] = sh.getDataRange().getValues();
Logger.log(JSON.stringify(vs));//view 2d array that stores your data
return vs
}
You can call it in a window.onload using google.script run or use templated html to load the data server side prior to rendering.
There are many examples of this on SO.
I think the easiest approach is using Templated HTML. It allows you to easily use your Google Apps Script functions inside your HTML, like ejs or Jinja2.
Code.gs
/* Sheet where the data lives */
const sS = SpreadsheetApp.openById('<SS_ID>').getSheetByName('StoreData')
/* Function to extract the data */
const getStoreData = () => sS.getDataRange().getValues().slice(1)
/* HTTP/GET function (For using template HTML you need to evaluate your file) */
const doGet = () => HtmlService.createTemplateFromFile('Index').evaluate()
And from using the getStoreData inside the HTML use the <? ?>, syntax.
Index.html
<div class="store-container">
<? getStoreData().forEach((item) => { ?>
<div>
<img src="<?= item[1] ?>">
<span>Price: <?= item[2] ?></span>
<span>Id: <?= item[0] ?> </span>
</div>
<? }) ?>
</div>
After this, you only need to style it as you need.
Documentation:
SpreadsheetApp
openById
getDataRange()
getValues()
I believe your goal is as follows.
You want to retrieve the values from the Spreadsheet and create the situation you showing with Web Apps.
When the value of the price is changed on Web Apps and click the save button, you want to update the column "C" of the Spreadsheet.
In this case, how about the following sample script?
Usage:
1. Sample script:
Google Apps Script side: Code.gs
Please copy and paste the following script to the script editor of Google Spreadsheet as a script. And, please set the sheet name.
const sheetName = "Sheet1"; // Please set the sheet name.
function doGet() {
const split = 4; // When this value is changed, the number of columns can be changed.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const values = sheet.getRange("A2:C" + sheet.getLastRow()).getValues();
let res = [];
do {
const temp = values.splice(0, split).reduce((s, [a, b, c]) =>
s += `<td><img src="${b}"><br>${a}<br><input type="number" name="saveValue" value="${c}"><input type="button" value="save" onclick="save();"></td>`
, "");
res.push(`<tr>${temp}</tr>`);
} while (values.length > 0);
const html = HtmlService.createTemplateFromFile("index");
html.table = res.join("");
return html.evaluate();
}
function saveValues(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
sheet.getRange(2, 3, e.length, 1).setValues(e);
}
HTML & Javascript side: index.html
Please copy and paste the following script to the script editor of Google Spreadsheet as a HTML.
<table><?!= table ?></table>
<script>
function save() {
const obj = document.getElementsByName("saveValue");
const values = [...obj].map(e => [e.value]);
google.script.run.saveValues(values);
}
</script>
2. Deploy Web Apps.
The detailed information can be seen at the official document. In this case, it supposes that you are using new IDE. Please be careful this.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
Please select "Anyone" for "Who has access".
For testing this script, I thought that this setting might be suitable.
Please click "Deploy" button.
Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
3. Testing.
Please access to the Web Apps using the browser. By this, the following result is obtained.
Sample Spreadsheet:
Sample Web Apps:
For example, when you change the price from 100 to 101 and click the save button, the column "C" of Spreadsheet is updated.
Note:
This is a simple sample script for answering your question. So, please modify this for your actual situation.
References:
Web Apps
HtmlFormApp
Taking advantage of Web Apps with Google Apps Script
Related
I have a spreadsheet I'm using to manage a bunch of content, with a script I've written that adds an "Export" button to the menu. When the button is clicked, the script gets all the appropriate data and formats it all in a specific way. The formatted version is saved to my Google Drive with a timestamp but a download link is also provided. I'll include a simplified version of the script below in case modifications are required.
I rarely ever use Google's Apps Scripts so I'm rather unfamiliar with the ins and outs of it. I only know the basics (how to write a script that can run when something is done from the spreadsheet's page).
I'm aware I can invite a user to my spreadsheet (or just make it public) but that doesn't seem to bring the script along with it. The script and all the formatting that's being done is the main part of what the person I'm inviting needs. I'm aware that for file.getDownloadUrl() to work (assuming the file is still saving on my Drive), I'd need to give the individual access to that folder as well which isn't a problem.
The question is, how do I give them access to the script so they get the Export menu item? Am I not able to? Am I basically limited to creating a button with the export function bound to it?
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var csvMenuEntries = [
{
name: "Export as CSV",
functionName: "csvExport"
},
{
name: "Export for wiki",
functionName: "wikiExport"
}
]
ss.addMenu("Export", csvMenuEntries)
}
function prepare(type) {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const ssName = ss.getName()
const sheet = ss.getSheets()[0]
const sheetName = sheet.getSheetName()
const folderName = ssName + ' exports'
let folder
try {
folder = DriveApp.getFoldersByName(folderName).next()
} catch (err) {
folder = DriveApp.createFolder(folderName)
}
let fileName
if (type) {
const extension = type === 'csv' ? 'csv' : 'txt'
fileName = ssName + '_' + sheetName + `_${type}_` + new Date().getTime() + `.${extension}`
}
return { ss, ssName, sheet, sheetName, folder, fileName }
}
function download(file) {
const downloadURL = file.getDownloadUrl().slice(0, -8)
showUrl(downloadURL)
}
function showUrl(downloadURL) {
var link = HtmlService.createHtmlOutput(`Click here to download`)
SpreadsheetApp.getUi().showModalDialog(link, 'Your file is ready!')
}
function csvExport() {
const { ss, sheet, folder, fileName } = prepare('csv')
const csvSettings = getCsvSettings(ss)
const csvFile = convertRangeToCsv(sheet, csvSettings) // not going to share this. It's simple but irrelevant
const file = folder.createFile(fileName, csvFile)
download(file)
}
function wikiExport() {
const { sheet, folder, fileName } = prepare('wiki')
const wikiFile = convertRangeToWikiFormat(sheet) // not going to share this. It's simple but irrelevant
const file = folder.createFile(fileName, wikiFile)
download(file)
}
A container-bound script has the same access as its parent spreadsheet, so if you're sharing the spreadsheet you're also sharing the script (though if they have only view access they have to create their own copy to see it):
All container-bound scripts use the same owner, viewer, and editor access list defined for the container file.
With that in mind, there are a few limitations when using scripts. First, they will not trigger for anonymous users (i.e., users that are not signed in), even if the sheet is editable to the public. You'll notice that if you try to open the script editor as anonymous, you will be asked to sign in. There's also a feature request to allow this on Google's issue tracker here.
Secondly, even if the users are signed in, there are other restrictions for Apps Script's triggers:
onOpen(e) runs when a user opens a spreadsheet, document, presentation, or form that the user has permission to edit.
Users need permission to edit the file for the onOpen() trigger to run. If they have viewer or commenter access the menu won't show up. In fact, you'll find that most script functions won't work if the users have only viewer access since they need editor access for most interactions with the sheet.
So if you want this menu to show up you'll need to give your users explicit editor access. If you really must keep your sheet as view-only or want to interact with anonymous users you can consider building a Web App instead and have the users get the download link from there. The web app has ways to communicate with the back-end or the Sheet so you should be able to reproduce your current code that way as well.
References:
Web Apps
Communicating with server functions
Triggers
I'm working on a student information template and I'm wondering if it's possible to print all of the data for each student in one go? I used data validation on my spreadsheet to modify the student ID so that their data will be easily view and print. Because I have to print it one by one for each pupil, this is a time-consuming process so I come up with this kind of flow to save time. Is it possible?
Please see the sample spreadsheet.
Upon printing, there should be a different page per student.
I believe your goal is as follows.
You want to reduce the cost of printing the data from the Spreadsheet.
I thought that when the Spreadsheet is printed out using Google Apps Script, the Google Cloud Print can achieve this. But I thought that in this case, the settings might be a bit complicated. So, in this case, I would like to propose a workaround. How about the following flow?
Retrieve the values from the source sheet and create the output values as the array.
Create a new Spreadsheet and put each value on each page.
Print out the Spreadsheet.
By this flow, you can print out all pages by one manual process.
The sample script is as follows.
Sample script:
Please copy and paste the following script to the script editor of Google Spreadsheet. And run myFunction.
function myFunction() {
const rowHeader = ["STUD ID", "LAST NAME", "FIRST NAME", "MIDDLE NAME", "NAME EXTENSION"];
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DATA");
const [header, ...values] = sheet.getDataRange().getValues();
const ar = values.map(r => header.reduce((o, h, i) => Object.assign(o, { [h.toUpperCase()]: r[i] }), {}));
const newValues = ar.map(e => [rowHeader, ...[rowHeader.map(f => e[f])]]).map(e => e[0].map((_, c) => e.map(r => r[c])));
const ss = SpreadsheetApp.create("tempSpreadsheet");
newValues.forEach((v, i) => (i == 0 ? ss.getSheets()[0] : ss.insertSheet()).getRange(1, 1, v.length, v[0].length).setValues(v));
}
When you run this script, a new Spreadsheet of tempSpreadsheet is created to the root folder. When you open it, you can see the expected values for each worksheet. By this, you can print out them.
Note:
This sample script is prepared from your sample Spreadsheet. So when your sample Spreadsheet is different from your actual situation, this sample script might not be able to be used. Please be careful about this.
References:
reduce()
map()
create(name)
I had a prob with my script, which was greatly answered in this question.
Basically custom functions cannot call services that require authorization. However, as far as I understood if I use simple triggers, such as onEdit it could work.
I checked the documentation suggested in the previous question, however I wasn't successful applying that to my code, which you can see below:
function FileName (id) {
var ss = DriveApp.getFileById(id);
return ss.getName();
}
How could I adapt my code to use simple triggers?
Here is a sample sheet that replicates the problem.
I believe your goal as follows.
You want to use your function of FileName as the custom function of Google Spreadsheet.
You want to automatically retrieve the filename when the file ID is put to the column "B".
You want to put the filename to the column "C".
Issue and workaround:
Unfortunately, when the custom function is used, in the current specification, the most methods except several methods (for example, one of them is UrlFetchApp.) that the authorization is required cannot be used. By this, DriveApp.getFileById(id) in your script cannot be used with the custom function. But there is a workaround. At the custom function, UrlFetchApp can be used. In this answer, I would like to propose to use the Web Apps with UrlFetchApp as the wrapper for authorizing. By this, the authorization can be done with the Web Apps. So your function can be run by the custom function.
Usage:
1. Prepare script.
Please copy and paste the following script to the script editor and save it.
const key = "samplekey"; // This is a key for using Web Apps. You can freely modify this.
// This is your function.
function FileName_(id) {
var ss = DriveApp.getFileById(id);
return ss.getName();
}
// Web Apps using as the wrapper for authorizing.
function doGet(e) {
let res = "";
if (e.parameter.key === key) {
try {
res = FileName_(e.parameter.id);
} catch (err) {
res = `Error: ${err.message}`;
}
} else {
res = "Key error.";
}
return ContentService.createTextOutput(JSON.stringify({value: res}));
}
function Filename(id) {
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set the URL of Web Apps after you set the Web Apps.
const res = UrlFetchApp.fetch(`${webAppsUrl}?id=${id}&key=${key}`);
if (res.getResponseCode() != 200) throw new Error(res.getContentText());
return JSON.parse(res.getContentText()).value;
}
2. Deploy Web Apps.
On the script editor, Open a dialog box by "Publish" -> "Deploy as web app".
Select "Me" for "Execute the app as:".
By this, the script is run as the owner.
Select "Anyone, even anonymous" for "Who has access to the app:".
In this case, the access token is not required to request to Web Apps. But in this sample script, a key for requesting to Web Apps is used.
Click "Deploy" button as new "Project version".
Automatically open a dialog box of "Authorization required".
Click "Review Permissions".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Click "OK".
Copy the URL of Web Apps. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
Please set the URL of https://script.google.com/macros/s/###/exec to url of above script. And please redeploy Web Apps. By this, the latest script is reflected to the Web Apps. So please be careful this.
3. Test this workaround.
When the file ID is put to the cell "A1", please put =filename(A1) to a cell as the custom function. By this, the script is run and the response value is returned.
Note:
Above sample script is a simple sample script for testing your script. So when you want to use the various methods, this post might be useful.
Please use this script with enabling V8.
As other method, I think that when the file ID is manually put to the column "B", the installable OnEdit trigger can be used. The sample script is as follows. Please set the sheet name. And please install the trigger to the function of installedOnEdit. Ref By this, when the file ID is put to the column "B" of sheetName, the file ID is put to the column "C".
function installedOnEdit(e) {
const sheetName = "Sheet1";
const range = e.range;
const sheet = range.getSheet();
if (!(sheet.getSheetName() == sheetName && range.getColumn() == 2 && range.getRow() > 1)) return;
const value = range.getValue();
let res = "";
try {
res = DriveApp.getFileById(value).getName();
} catch(e) {
res = e.message;
}
range.offset(0, 1).setValue(res);
}
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
Enhanced Custom Function for Google Spreadsheet using Web Apps as Wrapper
Related questions
Can you write a Google Sheets function that draws something?
Error when running Youtube Data Service in App Scripts (js) – Daily Limit for Unauthenticated Use Exceeded
How to enable not authorized users to protect the spreadsheet
Changing Owner of the Sheet irrespective of the duplicator
Installable Triggers
As you can draw from the documentation, simple triggers cannot access services that require authorization neither
You have to use installable triggers instead.
However the workflow is very different from custom functions.
In your specific case, you can implement e.g. that when a cell in column A is being edited (that is a new URL is being inserted) - the respective file name is being found and returned into column D.
You can retrieve the value and the row in which the new URL is being inserted with help of event objects.
Sample:
function FileName (event) {
var id = event.value;
var ss = DriveApp.getFileById(id);
var row = event.range.getRow();
var sheet = event.range.getSheet();
// for column D:
var column = 4;
var returnCell = sheet.getRange(row,column);
returnCell.setValue(ss.getName());
}
For using an installable onEdit trigger - bind it to this function through going on Edit > Current project's triggers as explained here.
I'm developing a Chrome extension that has access to Google Sheets using a standalone Google Apps Script.
I need help to be able to navigate and highlight given variable row from my extension. either using Apps Script or some other way.
I've tried this code in google apps script.
function navigateToRow(parameters) {
var ss = SpreadsheetApp.openByUrl(parameters.url)
var sheet = ss.getSheets()[0]
var range = sheet.getRange('A1:D10'); // will get range from parameters in future
range.activate();
}
There is another solution that could solve the problem, but it refreshes the page: using url parameters, e.g.
https://docs.google.com/spreadsheets/d/<spreadsheet id>/edit#gid=<sheet id>&range=<a1 notation>.
Try something like :
var range = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(1, 1);
SpreadsheetApp.setActiveRange(range);
Reference : https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#setactiverangerange
Solved using these lines of code inside the content-script.
hash = window.location.hash.split('&range=')[0]
hash += `&range=${currentRowInput.value}:${currentRowInput.value}#`;
window.location.hash = hash
Desired Outcome: To be able to enter a search term in a Google Form (presumably but not necessarily; could be a form in a standard web page) and have the relevant data retrieved from a Google Sheet and displayed in Google Site web app.
I learnt how to retrieve data from a parameterized URL and display in a Google Site in this question: How to include data in a URL for a Google Apps Script web app to read?
So the "tech" for retrieving and displaying spreadsheet data is there but I don't know where to start when it comes to pulling the data from a online form rather than a URL. Perhaps on submit, read the form values somehow, create a parameterized URL and go to that page to display the data?
How about this sample? This is a very simple sample script. Please modify it to your environment. This sample retrieves data on Spreadsheet using the search text, and displays the matched row. In order to use this sample, please carry out as follows.
Copy and paste the following scripts to your script editor.
Input spreadsheet ID and sheet name which is used for searching data.
Deploy Web Apps and run script.
Input search text and push "ok" button.
Script :
Google Apps Script : code.gs
function doGet() {
return HtmlService.createTemplateFromFile('index').evaluate();
}
function getData(e) {
var id = "### Spreadsheet ID ###";
var sheetname = "### Sheet name ###";
var data = SpreadsheetApp.openById(id).getSheetByName(sheetname).getDataRange().getValues();
var ar = [];
data.forEach(function(f) {
if (~f.indexOf(e.searchtext)) {
ar.push(f);
}
});
return ar;
}
HTML : index.html
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<form>
<input type="text" name="searchtext">
<input type="button" value="ok" onClick="getData(this.parentNode)" />
</form>
<pre id="disp"></pre>
<script>
function dispData(e) {
$('#disp').text(JSON.stringify(e));
}
function getData(e) {
google.script.run.withSuccessHandler(dispData).getData(e);
}
</script>
Sample spreadsheet :
Result :
If I misunderstand your question, I'm sorry.