Problems executing onEdit() trigger when another script edit the spreadsheet - google-apps-script

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 =)

Related

Google script, how to access and paste on edit to a specific sheet within another spreadsheet?

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

Set trigger in script in Google Sheets programmatically

I am trying to get a Google Apps Script to trigger on edit of a particular cell. Right now I have achieved this using a run-time trigger option built in Google Sheets. However, I lose the run-time trigger when I make a copy.
Hence, I have managed to find a programmable trigger from various sources and patched something together, but it is not working. I have no programming knowledge, so I am unable to understand where I am going wrong.
My objective is to run the script when user edits the Named Range "monthfilter".
With script trigger creation
function HideColumns() {
//open the current spreadsheet and get the value of the named range and trigger project
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("monthfilter").forSpreadsheet(ss).onedit()
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
My previous function:
function HideColumns() {
//open the current spreadsheet and get the value of the named range
var ss = SpreadsheetApp.getActive();
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
//show or hide February
if (namevalue == "February") {
var sheet = ss.getSheetByName("MIS");
sheet.hideColumns(30);
/** other stuff */
Not sure what is the 'runtime trigger' you mean, there are three kinds of trigger:
Simple trigger
Installable trigger
Time driven trigger
onEdit(e) is just a Simple trigger.
Let's take a look to the following situation:
yourMainSpreadsheet - (You are the owner)
|
binding script functions: - 1. function makeCopy( ) {...}
2. function monthFilter(e) {...}
3. function onEdit(e) { monthFilter(e); }
|
| After copying, new spreadsheet remains all functions and simple trigger.
V
newSpreadsheet- (You are the owner, maybe share with other editors.)
|
binding script functions: - 1. function makeCopy() {...}
2. function monthFilter(e) {...}
3. function onEdit(e) { monthFilter(e); }
No matter what kind of trigger you use or just manually run makeCopy() in yourMainSpreadsheet, onEdit(e) would be copied to new spreadsheet too, and it works when you or other editors make edits, you don't need to do anything:
function makeCopy(){
var mainSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSpreadsheet = mainSpreadsheet.copy('newSheet');
}
function monthFilter(e) {
var thisSheet = e.source.getActiveSheet();
var name = thisSheet.getRangeByName("rangeMonthfilter");
var nameValue = name.getValue();
//...
}
function onEdit(e) {
monthFilter(e);
}
What you have done is set a installable trigger to new spreadsheet.
Highlight of documentation about difference between them.
Installable triggers, however, offer more flexibility than simple
triggers: they can call services that require authorization ...
For example:
function monthFilter(e) {
var thisSheet = e.source.getActiveSheet();
var name = thisSheet.getRangeByName("rangeMonthfilter");
var nameValue = name.getValue();
// If you have operation like:
var onlyYouHavePermission = SpreadsheetApp.openById('xxxxxxxxx');
/* This is not allowed if you just use Simpler trigger onEdit(e) to
drive monthFilter(). */
}
For this, now is the time to use installable trigger:
function makeCopy(){
var mainSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var newSpreadsheet = mainSpreadsheet.copy('newSheet');
ScriptApp.newTrigger('monthFilter')
.forSpreadsheet(newSpreadsheet)
.onEdit()
.create();
/* The host of this trigger is you, it's like you
deploy a trigger to the newSpreadsheet, and make other editors
available to do something on "onlyYouHavePermission" through it.*/
}
Update
If all you want to do is just in HideColumns() you provide, you just need to add a new function which is a simpler trigger in script editor:
function onEdit(e) {
var ss = SpreadsheetApp.getActive();
var name = ss.getRangeByName("monthfilter");
var namevalue = name.getValue();
if (namevalue == "February") {
/* If you copy the spreadsheet, of course including the sheets
inside it, and you share the permission of the new spreadsheet to
other editors, because they own the permission, authorization
is not required here.
*/
var sheet = ss.getSheetByName("MIS");
sheet.hideColumns(30);
}
}

Adding a indication to script in Google Sheets

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;
}
...
}

Call to insertSheet works in Google Script Editor and was authorised but dosent work when calling from the spreadsheet

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...

Google Apps Script run when SpreadSheet is change by another Script

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