I am trying to create a script on Google Sheets to receive a message alert whenever the value of the B10 cell increases. Something like this:
Static oldVal As Variant
oldVal = Me.Range("B10").Value
If Me.Range("B10").Value > oldVal Then
Browser.msgBox('hello world');
End If
There is a way?
Other than your syntax being way off (Apps Script is based on JavaScript), yes, you can do that using a couple different mechanisms.
Use the onEdit(e) simple trigger to watch the sheet.
The e object has an oldValue and a value key you can compare directly. You can also test for a specific cell reference using the range parameter. The following tests for cell B10, specifically:
function onEdit(e) {
if (e.range.getA1Notation() == 'B10' && e.value > e.oldValue) {
Browser.msgBox("you increased the value")
}
}
Taking it even farther, you can specify a sheet within a Spreadsheet to test before the script will run.
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
if (e.range.getA1Notation() == 'B10' && e.range.getSheet().getName() == 'Página1' && e.value > e.oldValue) {
Browser.msgBox("you increased the value")
}
}
If the cell is not in the specified sheet or range,, nothing happens.
Watching a formula cell is a little more complex, but it will work. There are two methods in Apps Script. getValue() returns the value displayed in the cell, even if it's calculated by a formula. .getFormula() gets the cell's calculating formula. So, yes, this will work with simply calling .getValue() on the range.
The major difference is that you have to watch the formula cell because it is not directly edited by the user. In other words, the event object will not trigger if the cell value is calculated by a formula. Including it is easy:
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var formulaRange = e.range.getSheet().getRange("B4");
if (e.range.getA1Notation() == 'B10' || formulaRange && (e.range.getSheet().getName() == 'Página1' && e.value > e.oldValue) {
Browser.msgBox("you increased the value")
}
}
More on Apps Script event objects in the documentation.
Related
I have script for Google Sheets that I collected on interwebs and got some help here. No I have 2 onEdit in conflict. I overcome that by creating script Trigger for onEdit2. It works but I don't think it is the best solution. Could you help get those two separated onEdit with if functions into one, please?
//Dependent Dropdown list
function onEdit(e){ // Function that runs when we edit a value in the table.
masterSelector(master1,master2,master3,master4);
var activeCell = e.range; // It returns the coordinate of the cell that we just edited.
var val = activeCell.getValue(); // Returns the value entered in the column we just edited.
var r = activeCell.getRow(); // returns the row number of the cell we edit.
var c = activeCell.getColumn(); // returns the column number of the cell we edit.
var wsName = activeCell.getSheet().getName();
if (wsName === masterWsName && c === firstLevelColumn && r > masterNumberOfHeaderRows) { // the if delimits the section sensitive to modification and action of the onEdit.
applyFirstLevelValidation(val,r);
} else if (wsName === masterWsName && c === secondLevelColumn && r > masterNumberOfHeaderRows){
applySecondLevelValidation(val,r);
}
} // end of onEdit
// addRow by checkboxes
function onEdit2(e) {
masterSelector(master1,master2,master3,master4);
//IF the cell that was edited was in column 4 = D and therefore a checkbox AND if the cell edited was checked (not unchecked):
if (e.range.columnStart === 4 && e.range.getValue() === true) {
var sheet = SpreadsheetApp.getActiveSheet(),
row = sheet.getActiveCell()
.getRow(),
//(active row, from column, numRows, numColumns)
rangeToCopy = sheet.getRange(row, 1, 1, 30);
sheet.insertRowAfter(row);
rangeToCopy.copyTo(sheet.getRange(row + 1, 1));
//Reset checked boxes in column 4
sheet.getRange(row,4,2,1).setValue(false);
}
}
Whole script is here, if needed.
A script cannot contain two functions with the same name. Rename your first onEdit function to onEdit1 (actually it will be better to assign a descriptive name) and the second function as onEdit2, then put them both in one function named onEdit and pass the parameter e to both of them:
function onEdit(e){
onEdit1(e);
onEdit2(e);
}
Related:
Two OnEdit functions not working together
Best Practices for Multiple OnEdit Functions
How to run multiple onEdit functions in the same google script (google sheets)?
Bracketing multiple onEdit functions
In Google Apps Script for Google Sheets, I want to use a barcode scanner to input new data into a cell. If this new data matches what I am expecting, I want another cell to then be activated so I can continue to input data. This is being used to create an inventory, and the initial data being input (and what is being checked by the code) will be the name of the person using the scanner at that time. Here's what I have so far:
var ss = SpreadsheetApp.getActive()
var sheet1 = SpreadsheetApp.getActive().getSheetByName("Sheet1")
//Set active cell to A2 (cell below Name header) when file is opened.
function onOpen() {
sheet1.getRange('A2').activate();
}
function onEdit(e) {
if (e.range.getA1Notation() === 'A2') {
var nameval = sheet1.getActiveCell.getValue();
if (nameval == 'Alpha' || nameval == 'Beta') {
sheet1.getRange('D7').activate();
}
else {
sheet1.getRange('F1').activate();
}}}
Unfortunately this does not seem to do anything - The value in the cell is accepted but the new cell did not activate when either of the activation cases were input.
Modification points:
In your script, sheet1 is not declaread.
getActiveCell of sheet1.getActiveCell.getValue() is not run as the method.
I thought that nameval == 'Alpha' || nameval == 'Beta' might be written by includes.
When these points are reflected in your script, it becomes as follows.
Modified script:
function onEdit(e) {
var range = e.range;
var sheet1 = range.getSheet();
if (range.getA1Notation() === 'A2') {
var active = ['Alpha', 'Beta'].includes(range.getValue()) ? "D7" : "F1";
sheet1.getRange(active).activate();
}
}
In this modified script, when a value of 'Alpha' or 'Beta' is put to the cell "A2", the cell "D7" is activated. When a value except for 'Alpha' and 'Beta' is put to the cell "A2", the cell "F1" is activated.
Note:
This script is automatically run by the OnEdit simple trigger. So, when you directly run this script with the script editor, an error occurs. Please be careful about this.
References:
Simple Triggers
includes()
I'm trying to figure out an onEdit function for my Google Sheet where, when a checkbox in J16 of the DATA PULL sheet is checked, it will copy the values of the cells in columns F15 to F20 and I17 to E20, then paste them into a new row on the REPORTS sheets on the respective columns and once unchecked it will move to another row, and when checked it will copy the new data.
Really need some help.
Here is the file (https://docs.google.com/spreadsheets/d/19vw1mrwKcvUy66sxXV1r1e2eSlX-O862Ud-Fdi2iEQ8/edit?usp=sharing)
Sorry about that, I had to edit some confidential data and re-organize the design for easier on our end.
Creating function execution table
gs:
function onMyEdit(e) {//use installable so that you can perform actions requiring permissions
//e.source.toast('Entry')
const sh = e.range.getSheet();
if(sh.getName() == "Functions" && e.range.columnStart == 2 && e.value == "TRUE" && e.range.offset(0,-1).getValue() != null) {
//e.source.toast('working')
let n = e.range.offset(0, -1).getDisplayValue();
executeFunctionByName(n);
//Logger.log(n)
e.range.setValue("FAlSE"); //Reset checkbox
}
}
function func1() {
SpreadsheetApp.getActive().toast("func1")
}
function func2() {
SpreadsheetApp.getActive().toast("func2")
}
function executeFunctionByName(func) {
this[func]();
}
Functions Sheet:
Demo:
Description
Your data set is rather complex so I didn't produce a data set to test this but I'm pretty sure it will work for you.
1st. Test that we are on the checkbox in J16 and that the checkbox is true.
2nd. Get the values from I17:I20 a 2D array in the form [[1],[2],[3]...]
3rd. Get the value from E15:E20 a 2D array in the form [[4],[5],[6]...]
4th. Concatinate the 2 arrays [[1],[2],[3]...[4],[5],[6]...]
This would produce a column on a spreadsheet. However we can use the Array.flat() to make it a row [1,2,3...4,5,6...].
Lastly we simply copy the row to the other sheet but we need to convert to a 2D array to use setValues() by enclosing in array brackets [values].
Code.gs
function onEdit(e) {
let src = e.range.getSheet();
let dest = e.source.getSheetByName("Reports");
if( src.getName() === "Data Pull" ) {
if( ( e.range.getA1Notation() === "J16" ) {
if( e.value ) {
let values = src.getRange(17,9,4,1).getValues(); // I17:I20
values = values.concat(src.getRange(15,6,6,1).getValues()); // add E15:E20
dest.getRange(dest.getLastRow()+1,5,1,values.length).setValues([values.flat()]);
}
}
}
}
Reference
Event Objects
Sheet.getRange()
Range.getValues()
Range.setValues()
Array.concat()
Array.flat()
Here is the desired behavior:
Cell A1 contains a number (0 by default) representing damage taken.
Cells B1:D1 contain checkboxes (FALSE by default).
When cell B1 is edited (i.e. checked "TRUE"), cell A1 is decreased by 1. Cell B1 is automatically reset to FALSE. If cell A1 is decreased below 0, it is reset to 0).
When cell C1 is edited, cell A1 is incremented by 1 and automatically reset to FALSE.
When cell D1 is edited, cell A1 is reset to 0.
I'm very new to Google Apps Script and recently discovered Simple Triggers. It seems like onEdit is meant to trigger when anything in the entire workbook is edited, but I'm not sure. Is it possible for a script to run automatically if a specific range is edited (i.e. B1:D1 as described above)?
For context, I'm designing a character sheet for a play-by-post RPG. When a character takes damage, their capability in one or more skills is decreased by taking damage ranks. To make it as simple as possible to track these throughout the sheet, I'm trying to find a way to easily increment these ranks up and down without having to write a separate macro for each row.
If there's a better solution than the behavior I've proposed (e.g. using something other than checkboxes and onEdit triggers), please let me know! Also, it would be ideal if the solution also worked on mobile.
/*
Cell A1 contains a number (0 by default) representing damage taken.
Cells B1:D1 contain checkboxes (FALSE by default).
When cell B1 is edited (i.e. checked "TRUE"), cell A1 is decreased by 1. Cell B1 is automatically reset to FALSE. If cell A1 is decreased below 0, it is reset to 0).
When cell C1 is edited, cell A1 is incremented by 1 and automatically reset to FALSE.
When cell D1 is edited, cell A1 is reset to 0.
*/
function onEdit(e) {
var sh=e.range.getSheet();
e.source.toast('Entry');
if(sh.getName()!="Sheet1")return;//might need to change Sheet1 to whatever you're using
if(e.range.columnStart==2 && e.range.rowStart==1 && e.value=="TRUE") {
e.source.toast("B1");
var vA1=e.range.offset(0,-1).getValue();
e.range.offset(0,-1).setValue((vA1-1<0)?0:vA1-1);
e.range.setValue("FALSE");
}
if(e.range.columnStart==3 && e.range.rowStart==1 && e.value=="TRUE") {
e.source.toast("C1");
var vA1=e.range.offset(0,-2).getValue();
e.range.offset(0,-2).setValue(vA1+1);
e.range.setValue("FALSE");
}
if(e.range.columnStart===4 && e.range.rowStart==1 && e.value=="TRUE") {
e.source.toast("D1");
e.range.offset(0,-3).setValue(0);
e.range.setValue("FALSE");
}
}
Just a reminder: You can't run this function from the script editor just install it in a .gs file and play with your check boxes on Sheet1. If you not using Sheet1 the change the name of the sheet in the script to whatever you sheet name is.
It looks like this will work for "A1:D".
function onEdit(e) {
var sh=e.range.getSheet();
e.source.toast('Entry');
if(sh.getName()!="Sheet1")return;
if(e.range.columnStart==2 && e.value=="TRUE") {
e.source.toast("B1");
var vA1=e.range.offset(0,-1).getValue();
e.range.offset(0,-1).setValue((vA1-1<0)?0:vA1-1);
e.range.setValue("FALSE");
}
if(e.range.columnStart==3 && e.value=="TRUE") {
e.source.toast("C1");
var vA1=e.range.offset(0,-2).getValue();
e.range.offset(0,-2).setValue(vA1+1);
e.range.setValue("FALSE");
}
if(e.range.columnStart===4 && e.value=="TRUE") {
e.source.toast("D1");
e.range.offset(0,-3).setValue(0);
e.range.setValue("FALSE");
}
}
Modified a bit to help you test it
function onEdit(e) {
var sh=e.range.getSheet();
var cell=e.range.getA1Notation();//debug
e.source.toast('Entry');//debug
if(sh.getName()!="Sheet1")return;
if(e.range.columnStart==2 && e.range.rowStart<15 && e.value=="TRUE") {
e.source.toast(cell);//debug
var vA1=e.range.offset(0,-1).getValue();
e.range.offset(0,-1).setValue((vA1-1<0)?0:vA1-1);
e.range.setValue("FALSE");
}
if(e.range.columnStart==3 && e.range.rowStart<15 && e.value=="TRUE") {
e.source.toast(cell);//debug
var vA1=e.range.offset(0,-2).getValue();
e.range.offset(0,-2).setValue(vA1+1);
e.range.setValue("FALSE");
}
if(e.range.columnStart===4 && e.range.rowStart<15 && e.value=="TRUE") {
e.source.toast(cell);//debug
e.range.offset(0,-3).setValue(0);
e.range.setValue("FALSE");
}
}
Please note: there should only be one onEdit(e) simple trigger function. If fact there should only be one of any function in a .gs file.
You may wish to remove my e.source.toast(); lines
In This Example this trigger if for MainSheet, and cells of b, c and d are boolean type, not string:
function MainSheetOnEdit()
{
var spreadsheet = SpreadsheetApp.getActive();
var mysheet=spreadsheet.getActiveSheet();
if (mysheet.getSheetName()!='MainSheet') return;
if(mysheet.getCurrentCell().getColumn()==2)
{
if (mysheet.getCurrentCell().getValue()==true){
mysheet.getCurrentCell().setValue(false);
if(mysheet.getCurrentCell().offset(0, -1).getValue()>0)
{
mysheet.getCurrentCell().offset(0, -1).setValue(mysheet.getCurrentCell().offset(0, -1).getValue()-1);
}
}
}
if(mysheet.getCurrentCell().getColumn()==3)
{
if (mysheet.getCurrentCell().getValue()==true){
mysheet.getCurrentCell().setValue(false);
mysheet.getCurrentCell().offset(0, -2).setValue(mysheet.getCurrentCell().offset(0, -2).getValue()+1);
}
}
if(mysheet.getCurrentCell().getColumn()==4)
{
if (mysheet.getCurrentCell().getValue()==true){
mysheet.getCurrentCell().setValue(false);
mysheet.getCurrentCell().offset(0, -3).setValue(0);
}
}
}
My script works, for the first sheet of my sample spreadsheet. But it does not work for other sheets in my workbook, even though Logger.log(sheet.getName()) successfully fetches the name of the sheet that is currently being edited. It's a pretty simple problem, I just can't figure out why it only works for one of my sheets.
function onEdit (e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
Logger.log(sheet.getName())
if (e.range.getA1Notation() === 'C19' && e.value == 'TRUE') {
sheet.getRange('K2:K89').setNumberFormat("0.00oz")
}
else if (e.range.getA1Notation() === 'C19' && e.value == 'FALSE'){
sheet.getRange('K2:K89').setNumberFormat("0.00g")
}
In 'Loadout Picker' C19 values are TRUE/FALSE
In 'Copy of Loadout Picker 2.0' C19 values are 1/0
In the script in your if loops you have C19 == 'FALSE'/'TRUE'
That's the problem. Just remake a new tickbox at cell C19 in 'Copy of Loadout Picker 2.0' and it will reset the C19 values to TRUE/FALSE from 1/0. (Don't forget to rejoin the spreadsheet or just switch between the sheets so the code starts to work.)
That's also why onEdit() worked for the first sheet and not second.