onEdit(e) trigger won't call function automatically in google sheets - google-apps-script

I am trying to calculate some values using my own function (showing dummy function in this question), and I'd like the function to trigger when the user edits a cell in a given range, however even after writing the trigger the only time when the value is calculated again is when I go to the script editor and click Save.
My function:
function calculate(column) {
var sheet = SpreadsheetApp.getActiveSheet();
var column = sheet.getActiveCell().getColumn();
var values2d = sheet.getRange(1, 1, 15).getValues();
var values = [].concat.apply([], values2d);
var testRange2d = sheet.getRange(1, column, 15).getValues();
var testRange = [].concat.apply([], testRange2d);
var sum = 0;
for (var i = 0; i < 15; i++) {
if (testRange[i] == true) sum += values[i]
}
return sum;
}
function onEdit(e) {
var activeCell = SpreadsheetApp.getActiveSheet().getActiveCell();
if (activeCell.getBackground() === "#e3f5a2") activeCell.setBackground("#ffffff");
else activeCell.setBackground("#e3f5a2");
calculate();
}
And the sheet:
The cell besides total is where I call my function. And the value will only be updated either manually or as described earlier, but even when I trigger an event, I have also tried doing other actions in the trigger like changing a random cells value and that worked however.

Might i suggest the following. This will only occur if a checkbox in column 2 is changed.
function onEdit(event) {
try {
var sheet = event.source.getSheetByName("Sheet1")
if( event.range.getSheet().getName() === "Sheet1" ) {
if( event.range.getColumn() === 2 ) {
var values = sheet.getRange(1,1,15,2).getValues();
var sum = 0;
for( var i=0; i<values.length; i++ ) {
if( values[i][1] ) sum += values[i][0];
}
sheet.getRange(16,2,1,1).setValue(sum);
if( event.range.getBackground() === "#e3f5a2" )
event.range.setBackground("#ffffff");
else
event.range.setBackground("#e3f5a2");
}
}
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}

Related

Clearing values on gsheet with script

I am trying to clear some values in a defined named range taking into account also other value from another defined range of the same size. Here is my code below.
I get a Out of range on rng_stages.getCell(i,0).clearContent()
I suspect I no longer have a pointer to the sheet itself. As you can see I am not familiar with cell value assignment.
Does onOpen only triggers when the workbook opens or it also triggers each time a sheet is open in the same workbook? The named ranges only exist in the first sheet.
Here is the code:
function onOpen() {
var rng_stages = SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ClaimStages');
var rng_levels= SpreadsheetApp.getActiveSpreadsheet().getRangeByName('ClaimLevels');
var arr_stages = rng_stages.getValues();
var arr_levels = rng_levels.getValues();
for (var i = 0; i < arr_stages.length; i++) {
if ((arr_stages[i][0] == 'Approved') && (arr_levels[i][0] == -1)) {
rng_stages.getCell(i,0).clearContent()
}
}
}
function myFunction() {
const ss = SpreadsheetApp.getActive();
const rng_stages = ss.getRangeByName('ClaimStages');
const rng_levels = ss.getRangeByName('ClaimLevels');
const arr_stages = rng_stages.getValues();
const arr_levels = rng_levels.getValues();
for (let i = 0; i < arr_stages.length; i++) {
if ((arr_stages[i][0] == 'Approved') && (arr_levels[i][0] == -1)) {
rng_stages.getCell(i+1, 1).clearContent()
}
}
}
Range.getCell(row, column)
Rows and Columns begin at 1

Google Sheet script to hide columns by date

I'm essentially trying to do exactly what was done in this question, but with columns instead of rows. When I run the script there as is, it works fine. But just switching all references to columns to rows (and vice versa) isn't working for me, for some reason, and I can't figure out what's wrong.
For reference, this is what I have:
function onOpen()
{
var ui = SpreadsheetApp.getUi();
ui.createMenu('My Tools')
.addItem('Hide Columns','hideColumnsDate')
.addToUi();
}
function hideColumnsDate(row)
{
var row = (typeof(row) !== 'undefined') ? row : '1';
var day = 86400000;
var today = new Date().getTime();
var rng = SpreadsheetApp.getActiveSheet().getRange(row + ':' + row);
var rngA = rng.getValues();
for(var i = 0; i < rngA.length ;i++)
{
if(isDate(rngA[i][0]) && (((today - new Date(rngA[i][0]).getTime())/day) > 7 ))
{
SpreadsheetApp.getActiveSheet().hideColumns(i + 1);
}
}
}
function isDate (x)
{
return (null != x) && !isNaN(x) && ("undefined" !== typeof x.getDate);
}
When your script is modified, how about this modification? Please think of this as just one of several modifications.
Modification points:
In your situation, the values retrieved from the range of SpreadsheetApp.getActiveSheet().getRange(row + ':' + row) is [[column1, column2, column3,,,]].
In this case, the length of the for loop is rngA[0].length.
In order to retrieve the values of the columns, please modify rngA[i][0] to rngA[0][i].
Modified script:
From:
for(var i = 0; i < rngA.length ;i++)
{
if(isDate(rngA[i][0]) && (((today - new Date(rngA[i][0]).getTime())/day) > 7 ))
To:
for(var i = 0; i < rngA[0].length ;i++)
{
if(isDate(rngA[0][i]) && (((today - new Date(rngA[0][i]).getTime())/day) > 7 ))
Note:
In your case, as other pattern, you can also use the following modification.
Modify var rngA = rng.getValues(); to var rngA = rng.getValues()[0];, and modify rngA[i][0] to rngA[i].
In above modified script, all rows of a column are checked. If you want to check the specific columns, please tell me.
Reference:
getValues()
If this was not the result you want, I apologize.
function hideColumnsDate(row) {
var row=row||1;
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getRange(row,1,1,sh.getLastColumn());
var vA=rg.getValues()[0];
var today=new Date().valueOf();
vA.forEach(function(e,i){
var rowdate=new Date(e).valueOf();
if(((today-rowdate)/86400000)>7) {
sh.hideColumns(i+1);
}
});
}

How to check whether a new value is in a column?

I want to check for new names in a top 30 ranking from an API that refreshes daily, and then append every new name to an other column if it isn't already in there.
I think a for-loop would be the solution. This is what I got so far.
function appendValues(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var top30Names = ss.getRange("A4:A33").getValues();
var eligibleNames = ss.getRange("P4:P300").getValues();
for (i = 0; i < 30; i++){
var searchKey = top30Names[i]; // search if the eligible name is in the top30names
if (isInArray(searchKey, eligibleNames)){
// do nothing
}
else{
getFirstEmptyRow();
ss.getActiveCell().setValue(searchKey);
}
}
}
function isInArray(value, array) {
return array.indexOf(value) > -1;
}
function getFirstEmptyRow() {
var sheet = SpreadsheetApp.getActiveSheet(),
values = sheet.getRange("P4:P300") // the range to search for the first blank cell
.getValues(),
row = 0; //start with the first array element in the 2D array retrieved by getValues()
for (row; row < values.length; row++) {
if (!values[row].join("")) break;
}
return sheet.setActiveSelection("P" + (row + 4)).getRow();//.getLastRow() // column between "" and row + starting_row in range
}
This appends the full top 30 each time, but I only need the new values.
I've found a work around using setFormula. If anyone has a more elegant solution, I'd be happy learn.
function appendNewName(){
setFormula();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var newNames = ss.getRangeByName("newEntries").getValues();
for (i = 0; i < 30; i++){
getFirstEmptyRow();
var x = newNames[i][0];
// Logger.log(x); // What does this do???
if (x.length > 1) {
ss.getActiveCell().setValue(newNames[i]);
}
}
}
function setFormula(){
// first run this
clearFormula();
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getRangeByName("newEntries").setFormula("=IF(ISNUMBER(MATCH(A4,AppendNew,0)),\"\",A4)"); // Sets a formula to the range that will show the new daily entries in top 30
}
function clearFormula(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getRangeByName("newEntries").clear();
}
function getFirstEmptyRow() {
var sheet = SpreadsheetApp.getActiveSheet(),
values = sheet.getRange("appendNew") // the range to search for the first blank cell
.getValues(),
row = 0; //start with the first array element in the 2D array retrieved by getValues()
for (row; row < values.length; row++) {
if (!values[row].join("")) break;
}
return sheet.setActiveSelection("P" + (row + 4)).getRow(); // column between "" and row + starting_row in range
}

How do I pull a Row from an Array in Google apps script Google sheets

My spreadsheet is composed of a main sheet that is populated using a form plus several other sheets for the people who work with the responses submitted through the form. A script delegates the form responses to these other sheets depending on the type of item described in the response.
The problem is, when Person A deletes an item from their respective sheet, it doesn't delete in the main sheet.
My idea is that when you type a set password into the corresponding cell in row 'Q' in Person A's sheet, it matches the item by timestamp to the original form submission and deletes both the version of the item in Person A's sheet as well as the main sheet. However, I can't figure out what to set the range to to get it to point to the row in the array. Everything I have tried has sent back "undefined" in the debugger and won't delete anything. I think the problem is that I don't know how to get the row from the array that I have made. See my code below:
function onEdit() {//copies edited items from individual selector sheets back onto main spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actSheet = ss.getActiveSheet();
var responseSheet = ss.getSheetByName("Item Request");
var actCell = actSheet.getActiveCell();
var actRow = actCell.getRow();
var actVal = actCell.getValue();
var actLoc = actCell.getA1Notation();
var last = actSheet.getLastRow();
var respLast = responseSheet.getLastRow();
var dataA = responseSheet.getRange(1, 1, respLast, 1).getValues(); //compiles an array of data found in column A through last row in response sheet
var tstamp1 = actSheet.getRange(actCell.getRow(), 1);
var tsVal1 = tstamp1.getValue();
var colEdit = actCell.getColumn();
//===========THIS IS WHERE I'M STUCK=======================
if ((actVal == "p#ssword") && (colEdit == 17)) {
for (i = 1; i < dataA.length; i++) {
if (dataA[i][0].toString == tsVal1.toString()) {
responseSheet.deleteRow(i + 1);
actSheet.deleteRow(actRow);
break;
}
}
}
else if (colEdit == 15) { //checks the array to see if the edit was made to the "O" column
for (i = 1; i < dataA.length; i++) {//checking for timestamp match and copies entry
if (dataA[i][0].toString() == tsVal1.toString()) {
var toEdit = responseSheet.getRange(i + 1, 16);
toEdit.setValue(actVal);
}
}
}
else if (colEdit == 16) { // checks the array to see if the edit was made in the "P" column
for (i = 1; i < dataA.length; i++) {//checking for timestamp match and copies entry
if (dataA[i][0].toString() == tsVal1.toString()) {
var toEdit = responseSheet.getRange(i + 1, 17);
toEdit.setValue(actVal);
}
}
}
else {return;}
}//end onEdit
I don't believe these are proper commands delRow.deleteRow();actCell.deleteRow(); Take a look at the documentation;
Okay I rewrote that function for you a bit but I'm stilling wondering about a couple of lines.
function onEdit(e)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actSheet = ss.getActiveSheet();
var responseSheet = ss.getSheetByName("Item Request");
var actCell = actSheet.getActiveCell();
var actRow = actCell.getRow();
var actVal = actCell.getValue();
var colEdit = actCell.getColumn();
var respLast = responseSheet.getLastRow();
var dataA = responseSheet.getRange(1, 1, respLast, 1).getValues();
var tstamp1 = actSheet.getRange(actRow, 1);
var tsVal1 = tstamp1.getValue();
for(var i=0;i<dataA.length;i++)
{
if(new Date(dataA[i][0]).valueOf()==new Date(tsVal1).valueOf())
{
if (actVal=="p#ssword" && colEdit==17)
{
responseSheet.deleteRow(i + 1);
actSheet.deleteRow(actRow);
}
else if(colEdit==15)
{
var toEdit = responseSheet.getRange(i + 1, 16);//?
toEdit.setValue(actVal);//?
}
else if (colEdit == 16)
{
var toEdit = responseSheet.getRange(i + 1, 17);//?
toEdit.setValue(actVal);//?
}
}
}
}
Can you explain the function of the lines with question marked comments?

How to change a google spreadsheet row color, when a cell in the row is edited?

I have already tried this: Script to Change Row Color when a cell changes text but it can't get it to work. The color of the row does not change to #000000. This is what I have so far:
function onEdit(event)
{
var ss = event.source.getActiveSheet();
var r = event.source.getActiveRange();
var currentValue = r.getValue();
if(currentValue == "dags dato")
{
var dd = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd");
r.setValue(dd);
}
else if(currentValue == "dialog")
{
setRowColor("yellow");
}
else if(currentValue == "besvaret")
{
setRowColor("yellow");
}
else if(currentValue == "afvist")
{
setRowColor("red");
}
}
function setRowColor(color)
{
var range = SpreadsheetApp.getActiveSheet().getDataRange();
var statusColumnOffset = getStatusColumnOffset();
for (var i = range.getRow(); i < range.getLastRow(); i++) {
rowRange = range.offset(i, 0, 1);
status = rowRange.offset(0, statusColumnOffset).getValue();
rowRange.setBackgroundColor("#000000");
}
//Returns the offset value of the column titled "Status"
//(eg, if the 7th column is labeled "Status", this function returns 6)
function getStatusColumnOffset() {
lastColumn = SpreadsheetApp.getActiveSheet().getLastColumn();
var range = SpreadsheetApp.getActiveSheet().getRange(1,1,1,lastColumn);
for (var i = 0; i < range.getLastColumn(); i++) {
if (range.offset(0, i, 1, 1).getValue() == "Status") {
return i;
}
}
}
I wrote way faster and cleaner method for myself and I wanted to share it.
function onEdit(e) {
if (e) {
var ss = e.source.getActiveSheet();
var r = e.source.getActiveRange();
// If you want to be specific
// do not work in first row
// do not work in other sheets except "MySheet"
if (r.getRow() != 1 && ss.getName() == "MySheet") {
// E.g. status column is 2nd (B)
status = ss.getRange(r.getRow(), 2).getValue();
// Specify the range with which You want to highlight
// with some reading of API you can easily modify the range selection properties
// (e.g. to automatically select all columns)
rowRange = ss.getRange(r.getRow(),1,1,19);
// This changes font color
if (status == 'YES') {
rowRange.setFontColor("#999999");
} else if (status == 'N/A') {
rowRange.setFontColor("#999999");
// DEFAULT
} else if (status == '') {
rowRange.setFontColor("#000000");
}
}
}
}
You could try to check your code for any errors or issues by using the Logger class like so:
try {
//your code
}
catch(e) {
Logger.log(e);
}
Then you can go to View -> Logs from the Script Editor to see if each line of code performs as expected. Also the Execution transcript might be useful to see if the code breaks at one particular line of code. You can view more details about how each troubleshooting method works.