If I have the following user defined function I can return the text \"foo\"...
function myFunction(input){
return \"You wrote: \"+ input;
}
If I'm following correctly you are calling the apps script function using formula syntax, by putting =myFunction()
in a cell in your sheet.
In this case, there is no script based method to apply formatting to the cell containing the formula. Not only are you not automatically given a reference to the parent cell in your function, you also do not have permission to call set
methods (such as Range.setBackground()
) from within a custom function call inside a cell formula.
This is noted in the documentation under the Using Apps Script services heading:
Spreadsheet: Read only (can use most get*() methods, but not set*()). Cannot open other spreadsheets (SpreadsheetApp.openById() or SpreadsheetApp.openByUrl()).
https://developers.google.com/apps-script/guides/sheets/functions#advanced
The only thing your custom function (when placed in a cell as an automatically executed formula) can do, is return either a string or array, which will then be displayed in the sheet.
In case you have a user-triggered script, you can assign the color of a cell through code, while keeping everything in the script.
First: It is correct as @Cameron Roberts say's that a custom function executed automatically as a formula in a cell doesn't have access privileges to change formatting of any cells (including its own), or to access the Sheet to change the conditional formatting rules (since the Sheet is at a higher level).
I have focused on answering the highlighted parts here:
How can I assign a red background to the cell through code? Even though it's a simple example, my script is more complex and I want to assign formatting through code (I don't want to use the Format > Conditional Formatting... option; I need to have everything configured in a single script.
You can set conditional formatting rules through your script, which would allow you to keep everything configured in a single script: https://developers.google.com/apps-script/reference/spreadsheet/conditional-format-rule
If you have a complex script as you say, it is likely that the user has to initiate it somehow. Relying on that, you could do something like this, to change the formatting of a cell through code.
// This does not set the color of a user-selected cell like using getActiveCell does,
// but it necessarily relies on getActiveSheet(), since ranges are defined as referencing cells within sheets.
function setBackgroundOfScriptDeclaredCell() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A1");
range.setBackground("red");
}
// Will run when user opens/refreshes spreadsheet.
function onOpen() {
setBackgroundOfScriptDeclaredCell();
}
You can do the above, but you cannot set a cell to =setBackgroundOfScriptDeclaredCell()
to call that custom function from within the cell. It will give a “Exception: You do not have permission to call setBackground”. Due to reasonable limitations of what a custom function in a cell is allowed to access (only getters, not setters). See: https://developers.google.com/apps-script/guides/sheets/functions#advanced
// Add custom menu to spreadsheet
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Run custom script')
.addItem('Set background of a script-declared cell (A1)', 'setBackgroundOfScriptDeclaredCell')
.addToUi();
}
// This does not set the color of a user-selected cell like using getActiveCell does, but it necessarily relies on getActiveSheet(), since ranges are defined as referencing cells within sheets.
function setBackgroundOfScriptDeclaredCell() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A1");
range.setBackground("red");
}
You can set conditional formatting rules through your script, which would allow you to keep everything configured in a single script: https://developers.google.com/apps-script/reference/spreadsheet/conditional-format-rule This script could be executed automatically when the spreadsheet opens, or when user initiates your script (for instance through a menu option).
function yourScript() {
// ...
var cellsInA1Notation = "A1"; // could also have been e.g. "C3:D4"
setRangeToRedBackground(cellsInA1Notation);
// ...
}
// This is a custom convenience function I made which is not provided directly by the Google Sheets API.
function addConditionalFormatRule(sheet, rule) {
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules)
}
// Adds a conditional format rule to a sheet that causes a cell to have red background color if it contains a 1.
// To illustrate that conditional formatting rules do not need to be spread out across (and hidden) in the spreadsheet GUI,
// but can be manipulated entirely in your script.
function setRangeToRedBackground(cellsInA1Notation) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(cellsInA1Notation);
var customFormulaString = "=A1=1";
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenFormulaSatisfied(customFormulaString)
.setBackground("red")
.setRanges([range])
.build();
addConditionalFormatRule(sheet, rule);
}
Doing this, then you could make a function like:
function foo() {
return 1;
}
and put it into A1. Then when foo() executes in A1 and returns 1 then A1 will change its background color to red. This of course presumes that you know the result that foo() would output. The conditional formatting rule for A1 will already have been set by the script when the user opened the spreadsheet.
The benefit of doing something like this is that this shows that you can control a cell’s formatting through your script, even the conditional formatting rules (the most advanced of which is called “custom formulas”). This is advantageous if you want to contain every bit of rather complex code (including complex custom formulas) in your script. Maybe you want to be able to easily redistribute your script. Or maybe you just want to keep everything in one place. You also avoid having complex logic spread across and hidden in the Google Sheets GUI.
If you had used the GUI to manage your custom formulas used for conditional formatting, then to see and manage that logic, you would have to find and select the correct cell, or select the entire spreadsheet, and then right click and select “Conditional Formatting”, or go to the menu and select “Format -> Conditional Formatting”, to actually see and edit that logic. Which can be quite inconvenient at times.
I also wrote this up in a blog post where I went into more detail on two points:
SpreadsheetApp.newConditionalFormatRule().withCriteria
function, rather than with using only the special case .whenFormulaSatisfied
?Here: https://medium.com/@magne/how-to-format-cells-through-code-in-google-sheets-9d727ecc6053
I hope this answer may help someone else who might stumble across this challenge.
Source of inspiration for this answer, beside having roughly the same question myself: https://developers.google.com/apps-script/reference/spreadsheet/conditional-format-rule-builder