How to protect conditional formatting in google sheet? - google-apps-script

I have 2 CFs in a sheet that I would like to keep upon copy/pastes you can see the range and formulas below.
CF1 Range : B3:H1001
CF1 Formula : =($A3<>"")*(B3="") //This one higlights the empty cells in a given range
CF2 Range : A3:R1001 // This one highlights the row if the cell in R column is No.
CF2 Formula : =$R:$R="No"
Is there a way to protect these CFs or apply them again after copy paste ? Thanks !

When I copy paste from another range I am losing the Conditional formats that i have created. So I want them to stay like I formatted or I would like them to be applied again after the copy/paste.
From above replying, I could understand that you want to keep the conditional formats, which are =($A3<>"")*(B3="") for B3:H1001 and =$R:$R="No" for A3:R1001, even when the range is copied and pasted to B3:H1001 and A3:R1001.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Issue and workaround:
In the current stage, unfortunately, there are no methods for directly protecting the conditional formats in Google Apps Script. So in this case, as a workaround, I would like to propose to overwrite the conditional formats when the change of conditional formats and cells using OnChange event trigger.
Flow:
The range is copied and the parameters of conditional formats are changed.
The script is automatically run by the OnChange event trigger.
The existing conditional formats are deleted and new conditional formats are set.
Usage:
1. Copy and paste sample script
Please copy and paste the following sample script.
Sample script:
Please set the sheet name.
function onChange(e) {
const sheetName = "Sheet1"; // Please set the sheet name.
if (e.source.getActiveSheet().getSheetName() != sheetName || e.changeType != "OTHER") return;
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var rules = sheet.clearConditionalFormatRules();
const rule1 = SpreadsheetApp.newConditionalFormatRule()
.setRanges([sheet.getRange("B3:H")])
.setBackground("green")
.whenFormulaSatisfied('=($A3<>"")*(B3="")').build();
const rule2 = SpreadsheetApp.newConditionalFormatRule()
.setRanges([sheet.getRange("A3:R")])
.setBackground("green")
.whenFormulaSatisfied('=$R:$R="No"').build();
sheet.setConditionalFormatRules([rule1, rule2]);
SpreadsheetApp.flush(); // This line might not be required.
}
In this sample script, 2 conditional formats in your question are set as the background color of green.
2. Install OnChange event trigger
Please install the OnChange event trigger to the function of onChange.
3. Test run
As a test run, please copy and paste the range in sheetName. By this, the script is run by the OnChange event trigger.
Note:
I think that OnEdit event trigger can be also used. But in this case, when the parameters of the conditional formats are changed, the event trigger is not run. So I used the OnChange event trigger.
References:
Installable Triggers
setConditionalFormatRules(rules)

Related

Google Sheets, how to run function every x column?

I have a script in google sheets with the following function:
// Function to get current active sheet-name.
function getsheetName(dummy) {
return SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
}
Currently this function runs with this code in sheets:
=getsheetname(3:55)
The reference targets my entire sheet.
So whenever I make a change to a cell in my sheet the function runs again. This causes a huge amount of loading everytime i type anything in a new cell. Can i somehow reference the function to only run when i make changes to every x column instead of every cell?
I tried changing the formula but it would just stop updating completely, tried combining strings and using the MOD operator but I couldn't figure it out (keep in mind i am very new at this so probably missing something in my tinkering).
Appreciate any help!
I believe your goal is as follows.
You want to refresh the custom function of =getsheetname() when the columns "J", "T",,, (every 10 columns).
In this case, how about the following sample script? In order to refresh the function on Google Spreadsheet, I used this method. Ref
Sample script:
In this sample, in order to refresh your custom function, the simple trigger of onEdit is used. Please copy and paste the following script to the script editor of Spreadsheet. And, please save the script. When you use this script, please edit the columns "J", "T" and so on. By this, the script is run.
function onEdit(e) {
if (e.range.columnStart % 10 != 0) return;
const functionName = "=getsheetName";
const temp = "=temp";
const sheet = e.source.getActiveSheet();
sheet.createTextFinder(functionName).matchFormulaText(true).replaceAllWith(temp);
sheet.createTextFinder(temp).matchFormulaText(true).replaceAllWith(functionName);
}
In this case, 3:55 of =getsheetname(3:55) is not required to be used. You can use just =getsheetname().
If you changed the function name of your custom function, please modify const functionName = "=getsheetName";.
Reference:
Simple Triggers

Refresh a filter onEdit() in Google sheets when number is less than x

I have a sheet that we use as a cash log which is updated daily.
I want to limit the amount of rows that are visible to the person entering data. To that end I added a column (#12/L in this case) which applies the number 1 to each new row of data, and sequentially increases the existing rows.
I have a filter applied to Column L that limits viewing to rows that are less than or equal to 5. This accomplishes what I want, but I have to click Filter/Ok to update the view when new rows are added.
Is there a way I could create an onEdit script to monitor Column L for changes, and reapply a filter when a change happens?
I believe your current situation and your goal as follows.
The Spreadsheet has the basic filter at the column "L".
When the value of the column "L" is updated, you want to refresh the filter. For this, you want to use OnEdit trigger. - This is from your question of Is there a way I could create an onEdit script to monitor Column L for changes, and reapply a filter when a change happens?.
In this case, how about copying the existing basic filter of column "L" and removing it, and creating the copied filter again? I thought that by this flow, your goal might be able to be achieved. When this flow is reflected to the Google Apps Script, it becomes as follows.
Sample script:
Please copy and paste the following script to the script editor of Google Spreadsheet. And, when you want to run this script, please manually edit the cell of the column "L" on the sheet of "Sheet1". By this, the script is run. The script copies the existing basic filter of the column "L" and remove it, and create it as new filter. By this, the filter can be reflected to the new value.
function onEdit(e) {
const sheetName = "Sheet1"; // Please set the sheet name.
const range = e.range;
const sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 12) return;
const filter = sheet.getFilter();
const fRange = filter.getRange();
const criterion = filter.getColumnFilterCriteria(12).copy();
filter.remove();
fRange.createFilter().setColumnFilterCriteria(12, criterion);
}
Note:
This script is run by the OnEdit trigger. So when you directly run the script with the script editor, an error occurs. Please be careful this.
When the cell of the column "L" is updated by a script, in that case, I would like to recommend to include above method to the script. Because when the cell is updated by a script, the OnEdit trigger is not fired.
References:
Simple Triggers
Event Objects
Added:
From your following replying,
Thank you so much for your response. I'm definitely filing this one away for future use. I do realize that I was a little fast-and-loose with the word filter. Rather than a filter created as a result of a formula, I need to refresh a Filter View, i.e. one created by selecting the funnel and setting up a Filter by condition... An example of what I have can be viewed here: (docs.google.com/spreadsheets/d/…) Essentially nothing above the #5 (in Column L) should be visible after entering anything in Column A.
I understood that you wanted to refresh the basic filter of the column "L" when the column "A" is edited. From your question, I had thought that you wanted to refresh the basic filter of the column "L" when the column "L" is edited. But I understood that my understanding was not correct.
When you want to refresh the basic filter of the column "L" when the column "A" is edited, how about the following sample script?
function onEdit(e) {
const sheetName = "Sheet1"; // Please set the sheet name.
const range = e.range;
const sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 1) return;
const filter = sheet.getFilter();
const fRange = filter.getRange();
const criterion = filter.getColumnFilterCriteria(12).copy();
filter.remove();
fRange.createFilter().setColumnFilterCriteria(12, criterion);
}

Trigger bug when copying rows

I have a function that copies rows from one sheet to another, which works when I run it manually:
function updateDataRange() {
var formSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
var lastFormRow = formSheet.getLastRow();
var dataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DataRange");
dataSheet.clear();
for(var rowCounter = 1; rowCounter <= lastFormRow; rowCounter++) {
var sourceRange = formSheet.getRange('B'+rowCounter+':H'+rowCounter);
var insertRow = dataSheet.getLastRow() + 1;
var targetRange = dataSheet.getRange('A'+insertRow);
sourceRange.copyTo(targetRange);
}
}
However, when I run this via a trigger (e.g. function onEdit(e) { updateDateRange(); } ), there are gaps in the rows and some of the values will be left out. In this case, I know there is a "fix" by using rowCounter instead of insertRow to write the files instead of using getLastRow(), but one can easily see how this is a problem in another scenario.
So I guess my question is simple: Why isn't this working correctly when using a trigger?
Edit: To clarify, I have a 3rd sheet with some conditions/cells (i.e. conditions that influence which rows are to be copied) and changing them (like changing a different date) trigger the function.
Explanation:
You are using an onEdit trigger to capture sheet changes. But onEdit triggers work only when the user changes the value of a cell. Referencing the official documentation:
The onEdit(e) trigger runs automatically when a user changes the value
of any cell in a spreadsheet.
In your case, I think you are trying to execute this code when the Form Responses sheet is filled in with data by the form.
There are two kind of trigger functions that work with form submissions and they are both installable.
One is a google sheet event based trigger and the other one a form event based trigger.
Your goal is to execute some code from the google sheets side, so it makes sense to use the first one.
Modifications:
Change the name of the function from onEdit to a different name of your choice e.g. myFormSubmitTrigger.
Since the trigger is installable you need to "install" a onFormSubmit trigger for the myFormSubmitTrigger function. Here you can find some simple instructions on how to do that.
If your question is why doesn't your function work when the form makes changes to sheet Form Responses 1 then Marios has answered you question accept it and move on. If your trying to run your function when a user is making edits to sheet Form Responses 1 using a simple trigger then a possible explanation for not getting all of the rows completely copied is that simple trigger must complete in 30 seconds. If you continue to accept more data in Form 1 Responses then soon or later you will have problems but with this function it will be a lot later because it will run much faster than your code:
function updateDataRange() {
const sss=SpreadsheetApp.openById('ssid');
const ssh=e.source.getSheetByName('Form Responses 1');
const sr=2;
const svs=ssh.getRange(sr,2,sh.getLastRow()-ssr+1,7).getValues();
const dsh=e.source.getSheetByName('DataRange');
dsh.clear();
dsh.getRange(1,1,svs.length,svs[0].length).setValues(svs);
}
However, I would recommend that you never edit a form responses sheet. I would consider using the onFormSubmit trigger to capture all of the data to a second sheet that is available to edit. And it will have additional data automatically appended to it via dsh.appendRow(e.values). And so now you would no longer require an onEdit trigger because your data sheet is kept upto data with an onFormSubmit trigger and you may feel free to edit the datasheet any time you wish.
If neither of these questions suffices then I would recommend that you be more clear about what you are asking.

Trigger that fires when a new cell selection is made

Have successfully implemented OnEdit/OnChange triggers. However, these require an actual change to a cell.
It does not look like there is an analogous OnSelectionChanged trigger that fires when a new cell selection is made.
Any thoughts on how one might implement?
Good news. There is a new Simple Trigger, check this release note:
A new simple trigger, onSelectionChange(e), has been added for
Google Sheets. The onSelectionChange(e) trigger runs automatically
when a user changes the selection in a spreadsheet.
The following code illustrates how to use this trigger:
function onSelectionChange(e) {
var range = e.range;
if(range.getNumRows() === 1 && range.getNumColumns() === 1) {
SpreadsheetApp.getActiveSheet().clearFormats();
range.setFontColor("red").setFontWeight("bold")
}
}
If you add this function in your sheet bounded script the selected cell will always be highlighted as bold red. Please, note that this is just an example. The clearFormats() call will clear the format of all cells in the active sheet, so take care with it.

Trigger a script when a formula changes a cell value

I'm using a Google script then sends out an email when a certain column in a Google sheet is changed. The information in the cell is either inputted manually, or completes using a formula based on information in other cells.
The script works fine when information is manually entered, but not when the formula runs. I've read up on it and realise that a formula calculation doesn't count as an edit, so how do I get the script to run?
It's currently set up to trigger from the spreadsheet when there's an edit.
Below is the part of my script that covers the column/cell in question.
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var row = sheet.getActiveRange().getRow();
var cellvalue = ss.getActiveCell().getValue().toString();
if(sheet.getActiveRange().getColumn() == 12)
There's a lot more included in the script so I haven't copied everything onto here. Many thanks in advance.
There is no trigger that can run when a formula changes.
Try figure out, what conditions is your formula depends on:
if it is another cell, entered manually, then use those cell to trigger it's changes with onEdit
if the formula imports data from external source, use random or time functions, you'd better use onTime trigger.
if the formula uses importrange then go to the range you import and see the original range, return to step 1 → 2...