Using the code below I am able to generate a hyperlink to each page within my googlesheet, with the name of that sheet:
/**
* Gets the Sheet Name of a selected Sheet.
*
* #param {number} option 0 - Current Sheet, 1 All Sheets, 2 Spreadsheet filename
* #return The input multiplied by 2.
* #customfunction
*/
function SHEETNAME(option) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var thisSheet = sheet.getName();
var thisgid = sheet.getSheetId();
//Current option Sheet Name
if(option === 0){
return thisSheet;
//All Sheet Names in Spreadsheet
}else if(option === 1){
var sheet1 = [];
ss.getSheets().forEach(function(val){
sheet1.push(`=HYPERLINK("#gid=${val.getSheetId()}","${val.getName()}")`)
});
return sheet1;
//The Spreadsheet File Name
}else if(option === 2){
return ss.getName();
//Error
}else{
return "#N/A";
};
};
function GETLINK(option) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(option);
if (sheet != null) {
return(sheet.getId());
};
};
This returns a column of appropriate functions in each cells but does not evaluate the function. If I copy the function into another cell it does evaluate?!
=HYPERLINK("#gid=XXXXXXXXXXXXX","SHEET1")
I've tried also tried adding HTML but this also becomes a string and is not evaluated
sheet1.push(`${val.getName()})
When the custom formula of =SHEETNAME(0) is put to a cell, you want to put the sheet name of the active sheet on the Spreadsheet.
When the custom formula of =SHEETNAME(1) is put to a cell, you want to put the formulas of =HYPERLINK("#gid=XXXXXXXXXXXXX","SHEET1") for all sheets.
When the custom formula of =SHEETNAME(2) is put to a cell, you want to put the Spreadsheet name of the active Spreadsheet.
If my understanding is correct, how about this answer?
Issue and workaround:
Unfortunately, in the current specification of Google side, the formula cannot be put to a cell by the custom formula. When the formula is put to a cell by the custom formula, the formula is put as the string value. I think that this is the reason of your issue.
As a workaround, I would like to propose to put the formula of =HYPERLINK("#gid=XXXXXXXXXXXXX","SHEET1") using the OnEdit event trigger when the custom formula of =SHEETNAME(#) is put to a cell. I think that by this workaround, your goal can be achieved.
When your script is modified, please modify as follows.
Modified script:
In this case, the simple trigger can be used. So please copy and paste the following script to the script editor, and save the script. In order to use this script, as a test, please put =SHEETNAME(1) to a cell.
function onEdit(e) {
const spreadsheet = e.source;
const range = e.range;
const sheet = range.getSheet();
const formula = range.getFormula();
const f = formula.match(/\=SHEETNAME\((\d+)\)/i);
if (f && f.length > 0) {
if (f[1] == "0") {
range.setValue(sheet.getSheetName());
} else if (f[1] == "1") {
var formulas = spreadsheet.getSheets().map(val => [`=HYPERLINK("#gid=${val.getSheetId()}","${val.getName()}")`]);
range.offset(0, 0, formulas.length, 1).setFormulas(formulas);
} else if (f[1] == "2") {
range.setValue(spreadsheet.getName());
}
}
}
In this case, there is the function of SHEETNAME() in your script editor. So when =SHEETNAME(1) is put to a cell, at first, the function of SHEETNAME() is run, and then, the function of onEdit is automatically run by the OnEdit event trigger. If you don't want to show the values from SHEETNAME(), please replace the function of SHEETNAME() as follows.
const SHEETNAME = () => "";
Note:
In this case, when you directly run the function of onEdit at the script editor, an error occurs. Please be careful this.
This script is run under V8.
References:
Simple Triggers
Event Objects
setFormulas()
Related
I found this script:
function onEdit () {
var s = SpreadsheetApp.getActiveSheet ();
if (s.getName () == "sheet_name") {
var r = s.getActiveCell ();
if (r.getColumn () == 1) {
var nextCell = r.offset (0, 1);
if (nextCell.getValue () === '')
nextCell.setValue (new Date());
}
}
}
It works if I fill one cell by myself and puts current date to another cell in the right.
BUT if I use Zapier to export my data from Todoist to Google Sheets this script doesn't work. It only works if I change something manually.
Is there any way to make a script which will fill a cell I need with a today date when Zapier export data and fills cells automatically?
Suggestion:
As what Tanaike mentioned, you need to rename your function to something else aside from onEdit() since onEdit is a reserved function name for App Script and use onChange trigger.
But based on how Zapier works, the reason why the current code you have provided is not working is because exports from Zapier is not detected as an active cell, so we would need to revamp the entire code.
Try this instead:
function onZapierUpdate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet2'); //my test sheet is Sheet2
var range = sheet.getRange(2,1,sheet.getLastRow()-1, 2);
var data = range.getValues();
data.forEach(x => x[0] != "" ? x[1] = new Date() : x);
range.setValues(data);
}
After saving the script, set this on an onChange trigger like so:
Now whenever Zapier exports the data, it changes the content of the spreadsheet which means onChange trigger will take effect.
Reference:
https://developers.google.com/apps-script/reference/script/spreadsheet-trigger-builder#onChange()
I have a query in cell I25 of my first sheet in Googlesheets:
=query({SheetsRange("A13:F17")}, "select sum(Col6) where Col1 contains '"&D7&"' ")
The "SheetsRange" is an Appscript function (thanks to the author!) that gets all of the sheet names for me:
/**
* Returns concatened string range of all the sheets exept current sheet.
*
* #param {"A1:B5"} range - Input range as string that gets concatened to the sheetnames.
* #return {string} all the sheets with range together
* #customfunction
*/
function SheetsRange(range) {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const currentSheet = ss.getActiveSheet().getName()
const sheets = ss.getSheets()
const output = []
sheets.forEach(sheet => {
const sheetname = sheet.getName()
console.log(sheetname)
if (sheetname != currentSheet) {
const values = sheet.getRange(range).getValues()
values.forEach(row => {
output.push(row)
})
}
})
return output
}
Whenever I update a cell in a sheet, the sum in I25 should reflect these changes. However, it only does this when I hit save on the Appscript.
I think I need an onEdit(e) function in order to catch any changes made on the spreadsheet and call the SheetsRange function? however I've not been able to adapt examples I've found to work for me.
For your amusement, my current attempt of using onEdit is:
function onEdit(e) {
if (!e) {
throw new Error('Please do not run the script in the script editor window. It runs automatically when you edit the spreadsheet.');
}
if (e.range.getA1Notation() !== 'F12:F') {
return;
}
SheetsRange();
}
Thank you for any help!
In your situation, how about the following modification?
Modified script:
function onEdit(e) {
if (!e) {
throw new Error('Please do not run the script in the script editor window. It runs automatically when you edit the spreadsheet.');
}
// I modified below script.
const { range, source } = e;
if (range.columnStart != 6 || range.rowStart == 1) {
return;
}
var formula = "SheetsRange";
var tempFormula = "sample";
source.createTextFinder(formula).matchCase(false).matchFormulaText(true).replaceAllWith(tempFormula);
source.createTextFinder(tempFormula).matchFormulaText(true).replaceAllWith(formula);
}
In this modification, when the cells of column "F" is edited, your showing formula of =query({SheetsRange("A13:F17")}, "select sum(Col6) where Col1 contains '"&D7&"' ") is recalculated.
References:
createTextFinder(findText) of Class Spreadsheet
Class TextFinder
You don't really need onEdit() function to do this... you just need to change the concept of the script.
Custom functions do updates the output results while input got changes.
The reason why yours do not update is because your input is a plain text that doesn't change.
If you change the formula into this:
=LAMBDA(SHEETNAMES,RANGE,
LAMBDA(RLEN,CLEN,SROW,SCOL,
LAMBDA(NAMECOUNT,
LAMBDA(ARRAY,
QUERY(ARRAY,"SELECT SUM(Col6) WHERE Col1 CONTAINS'"&D7&"'")
)(
MAKEARRAY(RLEN*NAMECOUNT,CLEN,LAMBDA(R,C,
INDIRECT("'"&INDEX(SHEETNAMES,ROUNDUP(R/RLEN))&"'!R"&IF(MOD(R,RLEN)=0,RLEN+SROW,MOD(R,RLEN)+SROW)&"C"&C+SCOL,FALSE)
))
)
)(COUNTA(SHEETNAMES))
)(
REDUCE(0,REGEXEXTRACT(RANGE,"(\d+)\D+(\d+)"),LAMBDA(A,B,B-A))+1,
REDUCE(0,BYCOL(REGEXEXTRACT(RANGE,"(\D+)\d+:(\D+)"),LAMBDA(COL,COLUMN(INDIRECT(COL&"1")))),LAMBDA(A,B,B-A))+1,
REGEXEXTRACT(RANGE,"\d+")-1,
COLUMN(INDIRECT(REGEXEXTRACT(RANGE,"\D+")&"1"))-1
)
)(getSheetNames(),"A5:B10")
And change the script into this:
function getSheetNames() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const thisSheet = ss.getActiveSheet().getSheetName();
return ss.getSheets().map(sheet => sheet.getSheetName()).filter(name => name!==thisSheet);
}
Instead of using Apps-script to get the values of each sheet, this example only return a list of sheet names with it.
And we uses INDIRECT() in R1C1 format to create the import data array, which since it is a build-in spreadsheet formula, it is always dynamic.
The results should be updated whenever the reference sheets got updated even without onEdit() trigger.
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 want to create a script that will display the cell address of the active cell in A1, however, this is proving difficult because I can't even get the onSelectionChange example on the Google Apps Script guide to work. Whenever I run it, the error I get is TypeError: Cannot read property 'range' of undefined.
This is the code:
/**
* The event handler triggered when the selection changes in the spreadsheet.
* #param {Event} e The onSelectionChange event.
*/
function onSelectionChange(e) {
// Set background to red if a single empty cell is selected.
var range = e.range;
if(range.getNumRows() === 1
&& range.getNumColumns() === 1
&& range.getCell(1, 1).getValue() === "") {
range.setBackground("red");
}
}
About your error message of TypeError: Cannot read property 'range' of undefined, I thought that you might have directly run the function of onSelectionChange with the script editor. If it's so, such error occurs because the event object e is not given.
onSelectionChange is used as the simple trigger. In this case, when you want to retrieve the cell address of the active cell with onSelectionChange trigger, please select the cell on Google Spreadsheet. By this, the script is run by the onSelectionChange trigger.
Sample script:
For example, when the onSelectionChange trigger is run by selecting a cell, the sample script that the cell address is put to the cell as the A1Notation is as follows. When you use this script, please copy and paste the following script to the script editor of Spreadsheet and save it. And, please select a cell on the active Spreadsheet.
function onSelectionChange(e) {
var range = e.range;
range.setValue(range.getA1Notation());
}
Result:
When above script is used, the following result is obtained. You can see that in order to run the script, the cell is selected.
Note:
Even when you selected a cell, when the sample script is not run, please close the Spreadsheet and open the Spreadsheet again. In my environment, I confirmed that by this, the onSelectionChange trigger worked.
Reference:
onSelectionChange(e)
Added 1:
About the following your replying,
Thanks. This works and so did the red cell Google example. Although I did have to close the workbook and reopen it to get it to work. How can I set the range to a fixed cell so the active cell address only appears there?
When you want to run the script for only the specific range, how about the following sample script? From your replying, I used your script for this.
Sample script:
function onSelectionChange(e) {
var ranges = ["A2:B5", "D2:E5"]; // Please set the range you want to run the script.
var range = e.range;
var sheet = range.getSheet();
var check = ranges.some(r => {
var rng = sheet.getRange(r);
var rowStart = rng.getRow();
var rowEnd = rowStart + rng.getNumRows();
var colStart = rng.getColumn();
var colEnd = colStart + rng.getNumColumns();
return (range.rowStart >= rowStart && range.rowEnd < rowEnd && range.columnStart >= colStart && range.columnEnd < colEnd);
});
if (check) {
range.setBackground("red");
}
}
In this script, only when the cell in the range of var ranges = ["A2:B5", "D2:E5"] is selected, range.setBackground("red") is run. So please modify ["A2:B5", "D2:E5"] for your actual situation.
If you want to also check the sheet name, please modify above script as follows. In this case, when the cell in the ranges ["A2:B5", "D2:E5"] of the sheet ["Sheet2", "Sheet3"] is selected, range.setBackground("red") is run.
function onSelectionChange(e) {
var sheetNames = ["Sheet2", "Sheet3"]; // Please set the sheet names.
var ranges = ["A2:B5", "D2:E5"]; // Please set the range you want to run the script.
var range = e.range;
var sheet = range.getSheet();
if (!sheetNames.includes(sheet.getSheetName())) return;
var check = ranges.some(r => {
var rng = sheet.getRange(r);
var rowStart = rng.getRow();
var rowEnd = rowStart + rng.getNumRows();
var colStart = rng.getColumn();
var colEnd = colStart + rng.getNumColumns();
return (range.rowStart >= rowStart && range.rowEnd < rowEnd && range.columnStart >= colStart && range.columnEnd < colEnd);
});
if (check) {
range.setBackground("red");
}
}
Added 2:
About the following your replying,
No, sorry. I meant how can I make the active cell address appear in A1. If I click on cell G5, for example, 'G5' appears in A1.
In that case, how about the following sample script?
Sample script:
function onSelectionChange(e) {
var range = e.range;
range.getSheet().getRange("A1").setValue(range.getA1Notation());
}
Please see example picture below.
I would like to use Google Sheets script editor to add a Filter view on cell B3, that filters on a custom formula =(B4=$B$1).
The plan is to add a onEdit function that checks whether the value in cell B1 has changed, and if so reset the filter view in cell B3.
This is breaking my brain! I can't seem to find a way to auto update/refresh the filter once my value in B1 has changed.
My code currently looks like this, but this doesn't re-add the custom formula. It just keeps showing me all the non-empty rows.
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheetname = "Lactation";
var sheet = ss.getSheetByName(sheetname);
var erow = e.range.getRow();
//Logger.log("DEBUG: row = "+erow);
var ecolumn = e.range.getColumn();
//Logger.log("DEBUG: column = "+ecolumn);
if (erow == 1 & ecolumn == 2 ){
// there is a match so the cell is B2
//Logger.log("DEBUG: the cell is B2");
updatefilter();
}
else
{
// there is no match so the cell is NOT I3
//Logger.log("DEBUG: the cell is not I3");
}
}
function updatefilter() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('B3').activate();
var criteria = SpreadsheetApp.newFilterCriteria()
.setHiddenValues(['', 'No'])
.build();
spreadsheet.getActiveSheet().getFilter().setColumnFilterCriteria(2, criteria);
};
You want to modify the basic filter of the cells "B3:B" when the cell "B1" on the sheet Lactation is edited.
In this case, you want to use the OnEdit event trigger.
You want to use =(B4=$B$1), which is the custom formula, as the filter condition.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modification points:
You can take advantage of the event object as e of onEdit(e).
In order to use the custom function for the basic filter, you can use whenFormulaSatisfied.
Modified script:
In order to run the script, please edit the cell B1 on the sheet Lactation.
function onEdit(e){
var sheetName = "Lactation";
var editCell = "B1";
var range = e.range;
var sheet = range.getSheet();
if (range.getA1Notation() == editCell && sheet.getSheetName() == sheetName) {
updatefilter(sheet);
} else {
// do something
}
}
function updatefilter(sheet) {
sheet.getRange('B3').activate();
var criteria = SpreadsheetApp.newFilterCriteria().whenFormulaSatisfied("=(B4=$B$1)").build();
var filter = sheet.getFilter();
if (!filter) {
filter = sheet.getRange("B3:B").createFilter();
}
filter.setColumnFilterCriteria(2, criteria);
};
In this script, when the cell "B1" on the sheet of Lactation is edited, the function updatefilter is run by the OnEdit event trigger. And when the basic filter is existing, it is updated. When no filter is existing, the filter is set as new filter. The range of the filter is "B3:B". The custom function of =(B4=$B$1) is used as the criteria.
Note:
In above script, the simple trigger can be used. But if you use the methods for requiring to authorize, when an error occurs, please try to use Installable Triggers.
References:
Simple Triggers
Installable Triggers
Event Objects
whenFormulaSatisfied(formula)
If I misunderstood your question and this was not the direction you want, I apologize.