I'm trying to write a script that display row value in a SpreadSheet file using UiApp.
And this script will need to update the data whenever there is a change to the SpreadSheet. This would normally be simple with just Trigger onChange.
However, the SpreadSheet itself is also updated by another function, and it seem that onChange is not triggered when the changes made are caused by another AppScript.
Is there an alternative way to achieve what I described?
Code below, thanks
var ss = SpreadsheetApp.openById("tqwJk280I1O_yW5oNX9nLQA");
function doGet() {
var app = UiApp.createApplication();
var masPanel = app.createFlowPanel().setId("panel");
var number = parseInt(ss.getRange("A1").getValue());
masPanel.add(app.createLabel(number));
masPanel.add(app.createFlowPanel().add(app.createFormPanel()
.add(app.createFlowPanel()
.add(app.createSubmitButton("Plus!")))));
app.add(masPanel);
return app;
}
function doPost()
{
var num = parseInt(ss.getRange("A1").getValue()) + 1;
ss.getRange("A1").setValue(num);
}
function onChange()
{
ss.getRange("A2").setValue("hahaha");
var app = UiApp.createApplication();
app.remove(app.getElementById("panel"))
var masPanel = app.createFlowPanel().setId("panel");
var number = parseInt(ss.getRange("A1").getValue());
masPanel.add(app.createLabel(number));
masPanel.add(app.createFlowPanel().add(app.createFormPanel()
.add(app.createFlowPanel()
.add(app.createSubmitButton("Plus!")))));
app.add(masPanel);
return app;
}
The onChange() function will not have reference to the UI. For example, if there are multiple people who have the same UI open, which one should be updated ?
So, the solution to your problem is for your UI code to poll the spreadsheet regularly and update the UI if the spreadsheet has changed. To do so, you can use the undocumented addTimer() method.
See this SO question & answer for a nice example
Related
I am attempting to create a script so that when a change is made in one spreadsheet the corresponding sheet with the same name in another spreadsheet receives the same change in the same place, and so I can put one lot of the script in each one and get two linked sheets that affect each other.
this is the code:
var targetID = 'ID of the sheet';
var targetSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
function onEdit(e){
var range = e.range;
var value = e.value;
var row = range.getRowIndex();
var column = range.getColumnIndex();
exportValue(row,column,value)
}
function exportValue(row,column,value) {
//(**this is the point where it breaks**)
var s = SpreadsheetApp.openById(targetID).getSheetByName(targetSheet);
var target = s.getRange(row, column);
target.setValue(value);
}
In my version i put in logs in between each line to see where it failed and it got to the line where i have put "this is the point where it breaks" and then didn't return anything after that.
Since attempting this I tried to open up the other file by just pulling out all of the variables but I couldn't get it to work.
the current error messages that it is going with are:
Cannot find function getSheetByName in object Sheet
Cannot find function openById in object Spreadsheet
I have spent so much time on this already and I feel like the answer is really simple but I would really appreciate any advice
Thanks in advance
:)
Linked Spreadsheets with Installable onEdit() triggers
I got this to work by using an installable onEdit(e) Trigger.
function Linked1Edit(e){
var ss=SpreadsheetApp.openById('The Other Spreadsheets ID');
var sh=ss.getSheetByName(e.range.getSheet().getName());
var rg=sh.getRange(e.range.rowStart,e.range.columnStart);
rg.setValue(e.value);
}
I called one Linked1Edit(e) and the other Linked2Edit(e) and created an installable onEdit(e) trigger for each one and now they write to each other.
Unfortunately, it only works for single valued changes though.
The following script will allow you to make changes to more than one value at a time.
function Linked1Edit(e){
var ss=SpreadsheetApp.openById('The Other Spreadsheet ID');
var sh=ss.getSheetByName(e.range.getSheet().getName());
var rg=sh.getRange(e.range.rowStart,e.range.columnStart,e.range.rowEnd-e.range.rowStart+1,e.range.columnEnd-e.range.columnStart+1);
var vA=e.range.getValues();
rg.setValues(vA);
}
Event Object Link
Creating a Trigger
Select Current Project's Triggers
Click Add Trigger:
Create Trigger Dialog:
You can also create a trigger in code. See ScriptApp Class
I have a small script that saves data in Google Sheets. It looks at 3 cells and 'saves' them below. I have it set on a 'Project Trigger' every few hours and I also have a button. I would like to, in Column D, insert into the cell some text that indicates wether is was a trigger, or a manual button click ("Trigger","Manual").
What I get at the moment is:
URL : follower_count : date
I would like:
URL : follower_count : date : trigger_status
Here is the code:
// function to save data
function saveData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var url = sheet.getRange('Sheet1!A3').getValue();
var follower_count = sheet.getRange('Sheet1!B3').getValue();
var date = sheet.getRange('Sheet1!C3').getValue();
sheet.appendRow([url, follower_count, date]);
}
Thank You.
From the Google Apps Script documentation:
Simple triggers and installable triggers let Apps Script run a
function automatically if a certain event occurs. When a trigger
fires, Apps Script passes the function an event object as an argument,
typically called e.
Hence, when your function saveData is called by the trigger it will be invoked with an argument. This way you will be able to tell if its "Manual" or "Trigger". I.e:
function saveData(e) {
var isTrigger = false;
if(e){
isTrigger = true;
}
...
}
I have written a custom function that calls the code below to create a temporary sheet. When I ran it in Google Editor I was asked for authorisation and granted access.
My custom function runs well in Google Editor but doesnt work in the spreadsheet and I get the error "You do not have the permission to call insertSheet".
I created the spreadsheet and custom function so I own (have access) to everything.
var tempSheetName = "temp";
var sp = SpreadsheetApp.getActiveSpreadsheet();
var activeCell = sp.getActiveCell();
sp.insertSheet(tempSheetName);
From Google Apps Guide : Link
If your custom function throws the error message You do not have
permission to call X service., the service requires user authorization
and thus cannot be used in a custom function.
You may have your reasons, why you want to accomplish it in this way. But I'm really not sure if a custom function in a cell is the right place to create a new tab. You usually do calculations within a cell not document manipulations.
What, if you'd create a menu entry for the function like this and call it from there?
function onOpen() {
// instantiate UI object
var ui = SpreadsheetApp.getUi();
// create the menu
ui.createMenu("Functions")
.addItem("Create Sheet", "createSheet")
.addToUi();
}
function createSheet() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet(),
sheetName = 'mySheet',
sheet = ss.getSheetByName(sheetName);
// delete sheet if it already exists
if (sheet) ss.deleteSheet(sheet);
// insert sheet
ss.insertSheet(sheetName, 0);
}
Do you still get an error? Just curious...
Is there a way on to run a script whenever the user switches between sheets in a Google Sheets Spreadsheet?
More or less like onOpen, but instead of running when the document is opened, it should fire every time the user switches to another sheet.
Here is my work-around for onSheetChange:
function saveActiveSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actsheet = ss.getActiveSheet();
// The onSelectionChange() function executes in a separate thread
// which does not use any script global variables, so use the
// PropertiesService to maintain the user global state.
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('ACTIVE_SHEET', actsheet.getSheetName());
}
function onSheetChange(e) {
// Do anything needed after a new sheet/tab selection
}
function onSelectionChange(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Get current sheet name and compare to previously saved sheet
var currentactsheet = ss.getActiveSheet();
var currentactsheetname = currentactsheet.getSheetName();
var userProperties = PropertiesService.getUserProperties();
var actsheetname = userProperties.getProperty('ACTIVE_SHEET');
if (currentactsheetname !== actsheetname) { // New sheet selected
saveActiveSheet();
onSheetChange(e); // Call custom sheet change trigger
}
// Do anything needed when a different range is selected on the same sheet
else {
var range = e.range;
}
}
function onOpen(e) {
saveActiveSheet();
}
UPDATE: On April 2020 Google added onSelectionChange(e) which could be used to check if the user switched between sheets aka tabs.
At this time there isn't a trigger related to switch for one sheet to another. To learn about the available triggers in Google Apps Script, please checkout Triggers and events - Google Apps Script Guides
As a workaround you could a custom menu to and SpreadsheetApp.setActivesheet to switch between sheets by using a script instead of using tabs and including in that script a call to the function to be run when switching from one sheet to another.
I have created a "Feature request" to implement "onSheetChange" trigger at google issue tracker. You can star it, so google now you want this:
https://issuetracker.google.com/issues/72140210
And here is related question with possible workaround: How to capture change tab event in Google Spreadsheet?
you can use
sheet1.showSheet();
sheet2.hideSheet();
I have created an GUI app that allows a user to submit data and writes the data in a Spreadsheet. This Spreadsheet has the visibility option: "anyone with the link" and "Can edit" permission.
Also, there is a onEdit() trigger that should be executed everytime that a change has been made. There is no problem to write the data to the Spreadsheet but the trigger is not working.
Could you please assist me in this matter?
Many thanks in advance.
onEdit trigger is executed only when you manually change the contents of the table.
You can create general function and call it from UiApp and execute onEdit:
function edit(str){ //general onedit function
//do somethig (For example add " edited")
var cell = SpreadsheetApp.getActiveSheet().getActiveCell();
cell.setValue(str + ' edited');
}
function onEdit(){
var str = SpreadsheetApp.getActiveSheet().getActiveCell().getValue();
edit(str);
}
function gui(){
var app = UiApp.createApplication();
var tb = app.createTextBox().setName('tb');
var hndlr = app.createServerHandler('onSend').addCallbackElement(tb);
var btn = app.createButton('Send').addClickHandler(hndlr);
SpreadsheetApp.getActiveSpreadsheet().show(app.add(tb).add(btn));
}
function onSend(e){
var str = e.parameter.tb;
edit(str);
}
sorry for my bad english =)