I have a script which uses googlesheets, I am new to this also, there the trigger onOpen() and when the sheet is opened it runs whats inside it, however I wanted to run a function even when sheet is not opened. Do I need a trigger to do this? What should I use to do this?
I tried:
OnCreate
Installable
OnEdit
I want to create a time-based trigger will the script run, even if the sheet is not open and/or even if I'm not logged in to Sheets.
I tried:
function onOpen() {
// Trigger every 10 minutes.
ScriptApp.newTrigger('myFunc') //Run Update All Sheets
.timeBased()
.everyMinutes(10)
.create();
}
and outside the script. I wonder which of these works on background.
// Trigger every 10 minutes.
ScriptApp.newTrigger('myFunc') //Run Update All Sheets
.timeBased()
.everyMinutes(10)
.create();
function onOpen() {
}
It is possible to add a trigger programmatically.
https://developers.google.com/apps-script/guides/triggers/installable
Managing triggers programmatically
You can also create and delete triggers programmatically with the Script service. Start by calling ScriptApp.newTrigger(functionName), which returns a TriggerBuilder.
The following example shows how to create two time-driven triggers—one that fires every 6 hours, and one that fires every Monday at 9 a.m. (in the time zone that your script is set to).
triggers/triggers.gsView on GitHub
/**
* Creates two time-driven triggers.
*/
function createTimeDrivenTriggers() {
// Trigger every 6 hours.
ScriptApp.newTrigger('myFunction')
.timeBased()
.everyHours(6)
.create();
// Trigger every Monday at 09:00.
ScriptApp.newTrigger('myFunction')
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
}
This next example shows how to create an installable open trigger for a spreadsheet. Note that, unlike for a simple onOpen() trigger, the script for the installable trigger does not need to be bound to the spreadsheet. To create this trigger from a standalone script, simply replace SpreadsheetApp.getActive() with a call to SpreadsheetApp.openById(id).
triggers/triggers.gsView on GitHub
/**
* Creates a trigger for when a spreadsheet opens.
*/
function createSpreadsheetOpenTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('myFunction')
.forSpreadsheet(ss)
.onOpen()
.create();
}
To programmatically modify an existing installable trigger, you must delete it and create a new one. If you have previously stored the ID of a trigger, you can delete it by passing the ID as an argument to the function below.
triggers/triggers.gsView on GitHub
/**
* Deletes a trigger.
* #param {string} triggerId The Trigger ID.
*/
function deleteTrigger(triggerId) {
// Loop over all triggers.
var allTriggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < allTriggers.length; i++) {
// If the current trigger is the correct one, delete it.
if (allTriggers[i].getUniqueId() === triggerId) {
ScriptApp.deleteTrigger(allTriggers[i]);
break;
}
}
}
Open your google sheet
Click Tools
Click Script Editor
Make a function. Example Birth day greeting every day is shown below
function hbd_mail() { //birth day mail daily 7 am from hrd login
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Emp");
var today=new Date();
lastRow = sheet.getLastRow();
var values = sheet.getRange('A:T').getValues();
for (i=1;i<lastRow;i++){
if (values[i][14]=="") {} else {
if ( Utilities.formatDate( values[i][14] ,"GMT+05:30", "dd-MMM") == Utilities.formatDate( today ,"GMT+05:30", "dd-MMM") && values[i][18]=="Yes" ) {
var message = 'Dear '+ values[i][1]+',\n\nMegawin Collective wishes you a very happy birth day ('+ Utilities.formatDate( values[i][14] ,"GMT+05:30", "dd-MMM")+ ').\n\nHRD Megawin Switchgear Pvt Ltd';
var subject = "Happy Birth Day "+values[i][1];
if (values[i][7]=="") {} else {GmailApp.sendEmail(values[i][7], subject, message);}
}//if
}
}//for
}
Save the script
Click the Clock in the menu
It will ask you the function name - select the above function
Then select time based
Clcik every hour / day / week etc
Save it.
Now the function will run automatically from google server (at the frequency selected by you) without your having to do anything.
It is pretty simple.
Related
Can you please tell me how to automatically change the active sheet in google tables every day with Apps Script, like this: 1-2-3-1-2-3... And hide the inactive ones.
This is needed to show the current dining room menu.
Explanation:
You have three sheets in a spreadsheet file and your goal is to have only one sheet shown at a given day.
The first day you show only the first sheet and you hide the other two. The next day you hide all the sheets except for the second one and the third day you hide all sheets except for the third one.
The fourth day you start over again and you show only the first sheet.
This exercise needs two frameworks to work properly:
You need to store the day counter and the library you can use for that purpose is the PropertiesService class.
You need a time driven trigger to execute the script every day.
Solution:
The only thing you should do is to execute only and once the function createTrigger. The other function in this script is going to be executed every day automatically between 8-9 am and you should not execute it manually.
// execute only and once createTrigger
// Runs myFunction between 8am-9am in the timezone of the script
function createTrigger(){
ScriptApp.newTrigger("myFunction")
.timeBased()
.atHour(8)
.everyDays(1) // runs every day
.create();
}
// this should not be executed manually
function myFunction() {
const spreadsheet = SpreadsheetApp.getActive();
const sheets = spreadsheet.getSheets();
const sp = PropertiesService.getScriptProperties();
const breakPoint = 2; // after third sheet start over
const vs = sp.getProperty("visibleSheet");
let index;
if(vs){
index = parseInt(vs);
}
else {
index = 0;
sp.setProperty("visibleSheet",index);
}
sheets.map(sh=>sh.showSheet());
sheets.forEach((sh,i)=>{
if(i!=index){
sheets[i].hideSheet();
};
});
index==breakPoint ? sp.setProperty("visibleSheet",0)
: sp.setProperty("visibleSheet",index+1);
}
Things you can modify in the script:
Change 8 in atHour(8) if you want to execute the script in a different time rather than 8am.
Change variable breakPoint if you have more sheets (and not only 3). The counter starts from 0 this is why 2 indicates the third sheet. If you have six sheets then change it to breakPoint = 5;. In this way, the code is general and not restricted to the number of sheets.
Solution:
Apart from needing an Installable Trigger to have the script executed daily, this function should rotate the sheets per execution.
Prerequisites:
Take the Sheet ID of your spreadsheet from the URL and replace SHEET-ID in the code: https://docs.google.com/spreadsheets/d/**SHEET-ID**/edit#gid=0
Hide all sheets except one. The active sheet will be the starting point.
// execute this function first
function createTimeDrivenTriggers() {
// Trigger every 24 hours. Set preferred time here:
ScriptApp.newTrigger('myFunction')
.timeBased()
.atHour(10)
.everyHours(24)
.create();
}
// this should not be executed manually
function myFunction() {
var ss = SpreadsheetApp.openById("SHEET-ID");
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
if (!sheets[i].isSheetHidden()) {
sheets[(i+1) % sheets.length].showSheet();
sheets[i].hideSheet();
break;
}
}
}
I have this trigger that I want to run every morning at 9am.
function copyFormulasToValues() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[1];
var destination = ss.getSheets()[2];
var range = source.getRange("A1:C6015");
range.copyValuesToRange(destination, 1, 3, 1, 6030);
ScriptApp.newTrigger("myFunction")
.timeBased()
.atHour(9)
.nearMinute(1)
.everyDays(1)
.create();
The issue is, after a few days, my triggers fill up and it can't run anymore without me manually clearing them. I know there's a way to build in a function that essentially replaces the old trigger (by way of deletion) with the new trigger each day.
This post here gives an example of how to do it, but I just can't seem to get it to work. Would anybody be able to help me write this so that it deletes the existing trigger and replaces it with the new one each day? I'm a beginner so any help is much appreciated!!
Both of these methods will create a single trigger and replace a single trigger. If you need to create and replace multiple triggers, the code would be slightly different.
Method 1
Creates a new trigger, and replaces any existing triggers that are using the same event handler ("myFunction" in your example). This assumes that you won't change the name of your handler function between executions and that you don't have multiple triggers running with the same handler. It is the weaker of the two methods for those reasons. However, it is slightly easier to understand since it isn't using any services that aren't already in your code sample.
// this is the function that gets called by your trigger
function myFunction() {
Logger.log("I am an event handler");
}
function replaceTrigger(handlerName) {
const currentTriggers = ScriptApp.getProjectTriggers(); // get the projects triggers
const existingTrigger = currentTriggers.filter(trigger => trigger.getHandlerFunction() === handlerName)[0]
if (existingTrigger) ScriptApp.deleteTrigger(existingTrigger) // delete the existing trigger that uses the same event handler
// create a new trigger
if (existingTrigger[0])
ScriptApp.newTrigger(handlerName)
.timeBased()
.atHour(9)
.nearMinute(1)
.everyDays(1)
.create();
}
replaceTrigger('myFunction')
Method 2
Store a trigger ID when you create a trigger, then use that ID to delete it. The benefit of using this method is that it will allow you to change the name of your handler function, and it won't have side effects on other triggers in your project. It uses the PropertiesService, which you may be unfamiliar with.
function myFunction() {
Logger.log("I am an event handler");
}
function replaceTriggerWithId(handlerName) {
// read the existing trigger id from script properties and delete it if it exists
const currentTriggers = ScriptApp.getProjectTriggers(); // get the projects triggers
const existingTriggerId = PropertiesService.getScriptProperties().getProperty('triggerId');
if (existingTriggerId) {
const existingTrigger = ScriptApp.getProjectTriggers().filter(trigger => trigger.getUniqueId() === existingTriggerId)[0];
if (existingTrigger) ScriptApp.deleteTrigger(existingTrigger)
}
// create a new trigger and store its id in script properties
const triggerId =
ScriptApp.newTrigger(handlerName)
.timeBased()
.atHour(9)
.nearMinute(1)
.everyDays(1)
.create()
.getUniqueId()
PropertiesService.getScriptProperties().setProperty('triggerId', triggerId);
}
replaceTriggerWithId('myFunction');
You could set replaceTrigger or replaceTriggerWithId to run on a time trigger. When they are triggered they will delete any triggers that were defined with the same handler name (method 1), or whichever trigger was last defined by the trigger handler (method 2). After deleting the old trigger, they will create a new one.
References:
ScriptApp
PropertiesService
#RayGun 's answer method 2 seems Error , so I fixed up a little bit:
function myFunction() {
Logger.log("I am an event handler");
}
function replaceTrigger(handlerName) {
const currentTriggers = ScriptApp.getProjectTriggers(); // get the projects triggers
const existingTrigger = currentTriggers.filter(trigger => trigger.getHandlerFunction() === handlerName)[0]
if (existingTrigger) ScriptApp.deleteTrigger(existingTrigger) // delete the existing trigger that uses the same event handler
// create a new trigger
//if (existingTrigger[0]) Yes, I commented this
ScriptApp.newTrigger(handlerName)
.timeBased()
.atHour(9)
.nearMinute(1)
.everyDays(1)
.create()
.getUniqueId()
console.log('Create New Triger:...');
}
function main(){
replaceTrigger('myFunction')
}
Don't forget: you must Run 'main' function
, and then 'myFunction' will always be created only once, forever...
I created an installable trigger in a standalone script.
When I change something in the target spreadsheet, nothing happens. I don't get to a brekpoint in onEdit.
So how do I set up the trigger and onEdit to detect Edits?
ScriptApp.newTrigger('myOnEdit'){
.forSpreadsheet('1wfcYwChzLmbqxoXoSZUrXgZJIVbtEyNUIpfxmJihAcY')
.onEdit()
.create();
)
function onEdit(e){
// Set a comment on the edited cell to indicate when it was changed.
var range = e.range;
var ct = e.changeType
var ov=e.oldValue
var nv =e.value
var sc=e.triggerUid
range.setNote('Last modified: ' + new Date());
}
onEdit is a reserved function name to be used for on edit simple trigger. It should not be used for functions that will be called by an installable triggers as this could cause undesired effects as executing the function twice by a single event.
The installable trigger is being created to call a function named myOnEdit. In order to make it works your script should have a function named myOnEdit.
I want people to jump to the current date row when opening the sheet. However, this should be the case for everyone viewing the sheet and irrespective of their edit-rights. For example, I want that people edit the current date row in the sheet every day over the link. In this case, onOpen() does not work. Is there any alternative or modification to the function?
I am informed about onOpen trigger, however, this would not work if somebody is editing the sheet only over the link with edit rights.
This is e.g. the code I would like to work for everyone:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange("B:B");
var values = range.getValues();
var day = 24*3600*1000;
var today = parseInt((new Date().setHours(0,0,0,0))/day);
var ssdate;
for (var i=0; i<values.length; i++) {
try {
ssdate = values[i][0].getTime()/day;
}
catch(e) {
}
if (ssdate && Math.floor(ssdate) == today) {
sheet.setActiveRange(range.offset(i,0,1,1));
break;
}
}
}
I found the options: Edit Triggers
To manually create an installable trigger through a dialog in the script editor, follow these steps:
From the script editor, choose Edit > Current project's triggers.
Click the link that says: No triggers set up. Click here to add one now.
Under Run, select the name of function you want to trigger.
Under Events, select either Time-driven or the Google App that the script is bound to (for example, From spreadsheet).
Select and configure the type of trigger you want to create (for example, an Hour timer that runs Every hour or an On open trigger).
Optionally, click Notifications to configure how and when you are contacted by email if your triggered function fails.
Click Save.
Google Explanation to Edit Triggers
I have a container-bound script attached to a Google Sheet for which I've manually added an On edit installable trigger. I'm using an installable trigger rather than a simple trigger because I want the script to run from the sheet owner's account (my account) no matter who the user is who made the edit. The script is as follows:
function checkTimecardRange() {
var activeSS = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = activeSS.getActiveSheet();
var cell = sheet.getActiveCell();
var row = cell.getRow();
var approvalTimestamp = activeSheet.getRange("V" + row);
var timecardRange = activeSheet.getRange("A" + row + ":V" + row);
if(timecardRange.isBlank()==false) {
lockTimecardRange(timecardRange, row);
}
}
function lockTimecardRange(timecardRange, row) {
var protection = timecardRange.protect().setDescription('Timecard Row' + row);
protection.removeEditors(protection.getEditors());
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
It works fine when I run it manually, but the On edit trigger that I set up for it will not fire. I changed the trigger to a test script:
function log(){
console.log("Hello worlds");
}
and that doesn't fire either. Any assistance in getting the onEdit installable trigger to fire would be greatly appreciated. The edits based on which I expect the trigger to fire are manual edits (at this point I'm just typing in random values) and they are made from the account that owns the sheet and created the scripts.
Well, it turns out that it was a glitch on Google's side. All I had to do was delete the trigger, save, then re-add the trigger without any changes, and everything worked just fine. Go figure.