ActiveSheet().getName() returns different results. Could somebody explain why? [duplicate] - google-apps-script

I guess this question is the most similar to my current question. But basically, I have a google sheets file that has several sheets along the bottom. I would like for the user to execute this script on whatever sheet they have open at the time.
And like the previous question said according to this documentation, the active sheet is the one that is being displayed in the spreadsheet UI. I assume this means if I have the google sheets file open in another tab, that the sheet currently selected and viewable by me is intended to be the active sheet.
However, the actual active sheet returned is always the leftmost sheet along the bottom tabs. I've confirmed it by switching the order of the sheets. So no matter what sheet I actually have open and visible in the UI, it always gets the leftmost sheet. Here is the line that gets it.
var Sheet = SpreadsheetApp.openById("redactedid").getActiveSheet()
Is this a known error with App Script? Or is the documentation wrong? If it is, is there a workaround for getting the currently open sheet in the UI? I don't want anyone to have to hardcode a sheetname in the code as the file is constantly changing and sheets are being added.

Notes:
To use getActiveSheet() or any active object, the calling
Script must be bound to the Spreadsheet
Script function should be invoked from menu or button or macro keyboard shortcuts and NOT from webapp or API( Script editor also works, but not preferred).
Spreadsheet must be open/visible in the user interface/browser preferably in the active/currently selected window.
All previous calls to get objects must have used active. For eg, To get active range, You should have first got active spreadsheet => active sheet => active range(the chain of active objects). If any of the calls is not to the active object(say if you used getSheetByName() instead of getActiveSheet() in the middle step), the final call is likely to default to the default object(First sheet A1).
Solution:
Here, To get Spreadsheet, use SpreadsheetApp.getActive() instead of SpreadsheetApp.openById(), So that getActiveSheet() will be under the chain of active objects.
Snippet:
const sheet = SpreadsheetApp.getActive().getActiveSheet();

It's known that the getActiveSheet method returns the first sheet when used with an spreadsheet that is not the active spreadsheet.
The active spreadsheet is retrieved by using getActiveSpreadsheet. I only works on scripts bounded to an spreadsheet and in G Suite Editors add-on for spreadsheets.
In the the same way, getCurrentCell and getActiveRange returns A1 when corresponding sheet is not an active sheet (meaning it's not a sheet from an active spreadsheet)
Resources
https://developers.google.com/apps-script/guides/sheets
Related
Google Apps Script, select a sheet - Sets values in first sheet tab - not a specific sheet tab

All the points in the accepted solution are all true, but sometimes your spreadsheet looses some connection to something on Google's back end or the cache gets corrupted, who knows. The fix is to close the all tabs for the worksheet, editor, triggers, executions -- whichever ones you have open for that spreadsheet, close them. Reopen the spreadsheet. Start again. It's fine now.

Related

Google App Script getActiveSheet returns leftmost sheet, not active sheet

I guess this question is the most similar to my current question. But basically, I have a google sheets file that has several sheets along the bottom. I would like for the user to execute this script on whatever sheet they have open at the time.
And like the previous question said according to this documentation, the active sheet is the one that is being displayed in the spreadsheet UI. I assume this means if I have the google sheets file open in another tab, that the sheet currently selected and viewable by me is intended to be the active sheet.
However, the actual active sheet returned is always the leftmost sheet along the bottom tabs. I've confirmed it by switching the order of the sheets. So no matter what sheet I actually have open and visible in the UI, it always gets the leftmost sheet. Here is the line that gets it.
var Sheet = SpreadsheetApp.openById("redactedid").getActiveSheet()
Is this a known error with App Script? Or is the documentation wrong? If it is, is there a workaround for getting the currently open sheet in the UI? I don't want anyone to have to hardcode a sheetname in the code as the file is constantly changing and sheets are being added.
Notes:
To use getActiveSheet() or any active object, the calling
Script must be bound to the Spreadsheet
Script function should be invoked from menu or button or macro keyboard shortcuts and NOT from webapp or API( Script editor also works, but not preferred).
Spreadsheet must be open/visible in the user interface/browser preferably in the active/currently selected window.
All previous calls to get objects must have used active. For eg, To get active range, You should have first got active spreadsheet => active sheet => active range(the chain of active objects). If any of the calls is not to the active object(say if you used getSheetByName() instead of getActiveSheet() in the middle step), the final call is likely to default to the default object(First sheet A1).
Solution:
Here, To get Spreadsheet, use SpreadsheetApp.getActive() instead of SpreadsheetApp.openById(), So that getActiveSheet() will be under the chain of active objects.
Snippet:
const sheet = SpreadsheetApp.getActive().getActiveSheet();
It's known that the getActiveSheet method returns the first sheet when used with an spreadsheet that is not the active spreadsheet.
The active spreadsheet is retrieved by using getActiveSpreadsheet. I only works on scripts bounded to an spreadsheet and in G Suite Editors add-on for spreadsheets.
In the the same way, getCurrentCell and getActiveRange returns A1 when corresponding sheet is not an active sheet (meaning it's not a sheet from an active spreadsheet)
Resources
https://developers.google.com/apps-script/guides/sheets
Related
Google Apps Script, select a sheet - Sets values in first sheet tab - not a specific sheet tab
All the points in the accepted solution are all true, but sometimes your spreadsheet looses some connection to something on Google's back end or the cache gets corrupted, who knows. The fix is to close the all tabs for the worksheet, editor, triggers, executions -- whichever ones you have open for that spreadsheet, close them. Reopen the spreadsheet. Start again. It's fine now.

Can I use Google Apps Script to activate my spreadsheet?

I have an active spreadsheet, I want to select a cell in this spreadsheet and then bring me to another Spreadsheet. I used app.openByUrl to open the spreadsheet and then get my wanted sheet by name and then activate the cell by getRange, the script is finished but it didn't take me from the active spreadsheet to the other spreadsheet, the cursor remains in the cell of the active Spreadsheet. I use the following code. Does anyone has an idea?
app.openByUrl("https://docs.google.com/spreadsheets/d/1S9Kc6foLZ_bXqs4/edit#gid=502179704").getSheetByName("Verification Candidates").getRange('D4:F4').activate();
It's not possible to activate an external spreadsheet by solely using Google Apps Script as
Class Spreadsheet doesn't include like similar to Class Range activate
The Class Spreadsheet "open" methods open the spreadsheet on the server side, not on client side.
You might be able to do what you are looking by using other tools (i.e. a complex bookmarklet)

Switch from one spreadsheet to another in the same tab in Google Sheets with a script

I've been trying to build a script which I can use to switch from a given sheet in one spreadsheet, to a given sheet in another spreadsheet, in the same browser tab, where both spreadsheets are on the same google drive and in the same folder.
The purpose for doing this is I have more data than I can fit on a single google sheets spreadsheet (More than 200 individual sheets are needed) and I'd like to be able to navigate between them as seamlessly as possible.
I could have sworn I had a simple script like this working at some point, but I'm not sure. The script does appear to run, but it there is no result, it doesn't actually open the other sheet.
I've tried using both .openByID and .openByUrl, and both appear to give me the same result.
I set up two new spreadsheets, A and B, each with their own script, A pointing to B, and B pointing to A. The destination sheet within the spreadsheet has been named MainA and MainB.
//This is your "Go Home" Button
//URL of A: https://docs.google.com/spreadsheets /d/1LzsxZ3cCcOltELM_0T4VwipqYv5BbBDj9ugAZcciNHQ/edit#gid=0
//URL of B: https://docs.google.com/spreadsheets/d/1wrjr9VSsHb63RKz87JQbe20mCo1CteKHtsAiUGrb6O0/edit#gid=0
//ID of A: 1LzsxZ3cCcOltELM_0T4VwipqYv5BbBDj9ugAZcciNHQ
//ID of B: 1wrjr9VSsHb63RKz87JQbe20mCo1CteKHtsAiUGrb6O0
function goto_a() {
var ss = SpreadsheetApp.openById("1LzsxZ3cCcOltELM_0T4VwipqYv5BbBDj9ugAZcciNHQ")
ss.setActiveSheet(ss.getSheetByName("MainA"));
}
Obv the script for A is similar to this one, but with differently named function, destination sheet, and spreadsheet ID
In both cases, I'm using a button (drawing image) with the relevant script attached. If need be, I can share these spreadsheets so that the script can be tested.
Any advice would be great, thanks in advance.
As written in the official documentation ,
//Note that the spreadsheet is NOT physically opened on the client side.
// It is opened on the server only (for modification by the script).
You can alternatively provide a link using Browser.msgBox() for the user to click.
Related Question

Google Sheets - Replace sheet without breaking references to that sheet

We are building a google sheets database where each user has their own spreadsheet that accesses a central sheet for information using apps script.
This means that with 50 employees, we have 50 spreadsheets to maintain. I am trying to find a way to push updates to all 50 spreadsheets without having to update each one manually. I have all the apps script code in a library that each user's sheet references, so I have the coding maintenance figured out. But keeping each users actual spreadsheet up to date with the latest features is proving difficult.
One way I'm figuring to do that is have a "Template" user sheet that gets updated with the changes/new features. Then when each user opens their spreadsheet, it cross references all of its sheets to the template sheet, and checks if it needs to replace it's sheet with the latest sheet based on time that it was updated in the template sheet. For example, when the sheet "Project Report" in the template is newer than the "Project Report" sheet in the user's spreadsheet, the user SS deletes it's current "Project Report" and copies the template "Project Report" sheet to it's own via the copyTo() method.
I have this all working with apps script, but the issue now is that when the user's local sheet is deleted and replaced with the new updated seet, all formula references to that sheet in other sheets break and replace the reference with #REF. I had planned on overcoming this by using only Named Ranges, but even the named ranges break when the sheet is replaced to the point where even the apps script can no longer find the named range because the named range it is looking for was automatically renamed when the new version of the sheet was imported (aka, "CustomNamedRange" in the template SS was renamed to "'SheetName'!CustomNamedRange" in the user SS).
The only way I know to overcome this issue at this point is to create a centralized "Range Index" spreadsheet that has all the named ranges with their destination sheet and range. I would have to create a custom function that filters through the range index and finds the address it needs based on the name given. For example, instead of calling "CustomNamedRange" in a sheet formula, I would call custom function: getNamedRange("CustomNamedRange"), and apps script would return the range found in the range index. And when a sheet is replaced with the newer version, no references would break because all references go through the apps script filter function.
The only problem with this is that I can foresee this method (calling every range needed in the script through a custom function) slowing down my spreadsheet A LOT because every time a range is called for, it will have to go search through the range index to find it and return it.
Does anyone have any other ideas on how to accomplish what I'm looking for? As in keeping 50+ individual spreadsheets updated with new features without having to do it manually and without breaking all the references?
Sorry for the long post, but I appreciate any ideas!
I had a similar problem and was able to resolve it by using SheetAPI to replace text. I have a template called Sheet1_Template and its hidden. I delete Sheet1, copy Sheet1_Template, show it and then replace all occurances of "Sheet1" in formulas to "Sheet1". Sheet API has to be enabled in the Resources and Google API Console.
function copyTemplate() {
try {
var spread = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("Sheet1");
if( sheet !== null ) spread.deleteSheet(sheet);
sheet = spread.getSheetByName("Sheet1_Template");
sheet = sheet.copyTo(spread);
sheet.setName("Sheet1");
sheet.showSheet();
sheet.activate();
spread.moveActiveSheet(0);
var requests = {"requests":[{"findReplace":{"allSheets":true,"find":"Sheet1","replacement":"Sheet1","includeFormulas":true}}]};
Sheets.Spreadsheets.batchUpdate(requests, spread.getId());
}
catch(err) {
Logger.log("error in copyTemplate: "+err);
}
}
I haven't been able to test implementation of it yet, but I believe the answer above is what I was originally looking for.
I haven't spent any time messing with the API yet, so in the meantime I have found another solution:
Google Sheets recently added macros to it's feature set. The beauty of this is that You can see and edit the macro code after you've recorded your actions in the sheet. For now, I plan on recording a macro when I make updates to the template sheet, then copying the script for that macro into a custom function in my library that will run every time a user opens their spreadsheet. When they open their SS, apps script will check to see if the library's macro function has a later date than the last time the sheet was opened. If it does have a new date, then it will run the macro script, and that user's SS should get updated to the same state as the template.
Also if you are seeing that you cannot run the query from #TheWizEd
It may be due to "Sheets API" not being enabled at Advanced Google services. Please enable>
In the script editor, select Resources > Advanced Google services In the dialog that appears, click the on/off switch for Google Sheets API v4. Please turn on. Click OK button.
Thank you so much to TheWizEd for getting me started (please vote for that post too).
This is what I needed:
function replaceFormulasInSheet(sheet, searchFor, replaceWith) {
// https://stackoverflow.com/a/67151030/470749
// First you need to do this to enable the feature: https://developers.google.com/apps-script/guides/services/advanced#enabling_advanced_services
// https://developers.google.com/sheets/api/quickstart/apps-script
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#findreplacerequest
// https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/batchUpdate
const spread = SpreadsheetApp.getActiveSpreadsheet();
const requests = {
"requests": [
{
"findReplace": {
// "allSheets": true, Omitting this property and instead setting the sheetId property is the only way to effectively set allSheets as false.
"sheetId": sheet.getSheetId(),
"find": searchFor,
"replacement": replaceWith,
"includeFormulas": true
}
}
]
};
return Sheets.Spreadsheets.batchUpdate(requests, spread.getId());
}
Also note that it does not work for sheets with hyphens in their names. If you need hyphens in their names, remove the hyphens beforehand and re-add them after.

When to use getActiveSpreadsheet?

I'm trying to understand when to use getActiveSpreadsheet().
I've seen numerous examples:
var ss = SpreadsheetApp.getActiveSheet();
But I've also seen:
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
Is the active spreadsheet implied in some circumstances?
Yes, it is implied. The reference for SpreadsheetApp states:
getActiveSheet()
Gets the active sheet in a spreadsheet. The active sheet in a
spreadsheet is the sheet that is being displayed in the spreadsheet
UI.
Including the getActiveSpreadsheet() is probably often done because the getting of a spreadsheet is needed for other sheets and methods and it keeps the command flow consistent when done everywhere.
getActiveSpreadsheet() is a method used by container-bound script.
A script is bound to a Google Sheets, Docs or Forms file if it was created from that document rather than as a standalone script. The file a bound script is attached to is referred to as a "container". Bound scripts generally behave like standalone scripts except that they do not appear in Google Drive, they cannot be detached from the file they are bound to, and they gain a few special privileges over the parent file.
getActiveSpreadsheet(), getActiveDocument(), and getActiveForm() allow bound scripts to refer to their parent file without referring to the file's ID.
In Google Sheets, getActiveSheet(), getActiveRange(), and getActiveCell() let the script determine the user's current sheet, selected range of cells, or selected individual cell. setActiveSheet(sheet) and setActiveRange(range) let the script change those selections.
Taken from https://developers.google.com/apps-script/guides/bound
getActiveSheet() is usefull when you are working with one sheet in the active spreadsheet.
You would use getActiveSpreadsheet() when you need to use other methods for example getSheets() would get all the sheets and you can access them as an array. Or if you wish to work with the spreadsheet as opposed to the sheet itself.
A good example would be the methods available to the spreadsheet class which would not be available for the sheet class