I currently have a Google sheet conditionally formatted the way I need it. The problem is when I share the sheet, people often copy and paste cells messing up the conditional formatting rules. Is it possible to somehow store my preferred conditional format rules in a macro, then when someone edits the sheet, it will auto reset any changes to the preferred rules?
Try duplicating the sheet where you have the pristine conditional formatting rules in place. To restore the formatting:
Go to the pristine sheet.
Press Control+A once or twice to select all cells.
Press Control+C.
Go to the messy sheet.
Select cell A1.
Choose Edit > Paste special > Paste conditional formatting only.
The process can be automated with a script like this:
function resetMessyConditionalFormatting() {
const ss = SpreadsheetApp.getActive();
const pristineRange = ss.getSheetByName('Pristine').getDataRange();
const messyRange = ss.getSheetByName('Messy').getRange('A1');
pristineRange.copyTo(messyRange, SpreadsheetApp.CopyPasteType.PASTE_CONDITIONAL_FORMATTING, false);
}
I created a template with only the formatting.
var ss1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Formatting");
var ss2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ASR Tracker");
ss2.clearConditionalFormatRules()
ss1.getRange("A2:AI985").copyTo(ss2.getRange("A2:AI985"), {formatOnly:true});
Related
I use importrange for combine 2 google sheet Target A and Source B
In sheet A =
Importrange(“linkgooglesheetB”,”SheetB!!A2:M1000)
It’s done
But when data in sheet Source B cleared/changed, then
Data in sheet Target A cleared/changed too
What formula can use same my above formula but data in Target A do not change after combined (A&B), when B changed.
Answer:
You can do this with Google Apps Script.
Explanation:
The IMPORTRANGE formula will always automatically update when the range from which it is importing had a data change. In fact, this is a feature of all sheet formulae; they are designed to keep everything up-to-date when data changes.
For this reason, you can not use a formula. You will have to do this using Google Apps Script.
Example code:
The idea is as such:
When the sheet is edited, a script will run.
If the cell data matches a certain pattern, then the script will paste in the data permanently, in the same way that IMPORTRANGE works.
function runOnEdit(e) {
// We will make the patten here. In this case, the text entered in the cell must be of the form:
// "IMPORT(link,range)
// example:
// IMPORT,https://docs.google.com/spreadsheets/d/some-id/edit, SheetB!A2:M1000
const patternStart = "IMPORT"
const compareVals = e.value.split(",")
if (compareVals.length != 3 || compareVals[0] != patternStart) return
try {
const ss = SpreadsheetApp.openByUrl(compareVals[1])
const importRange = compareVals[2].split('!')
const sheetName = importRange[0]
const range = importRange[1]
const sheet = ss.getSheetByName(sheetName)
console.log(sheetName)
console.log(range)
const data = sheet.getRange(range).getValues()
const formatting = sheet.getRange(range).getTextStyles()
const activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const row = e.range.getRow()
const col = e.range.getColumn()
activeSheet.getRange(row, col, data.length, data[0].length).setValues(data).setTextStyles(formatting)
}
catch (err) {
e.range.setValue(err)
}
}
Things to note:
The pattern can not start with an = as this will be read as a formula by Sheets and throw an error.
This must be set up as an installable trigger, as opening a separate Spreadsheet requires authorisation, which can not be done from a simple trigger.
You can install the trigger by clicking on the clock logo in the left toolbar from the Apps Script editor, then clicking + Add Trigger in the bottom right and using the following settings:
Choose which function to run: runOnEdit
Which runs at deployment: Head
Select event source: From spreadsheet
Select event type: On edit
Visual example:
Most of the formula is live update, including query function. However in order not to have your data change for importrange, there is two possible solution:
1)Create a backup sheet by copy paste data from the main sheet, and importrange from the backup sheet, so any change in main sheet will not have impact on your result
2)Write Google App Scrip to copy data, but it take time to learn
I'm using arrayformulas to populate certain columns of my sheet, depending on values in other sheets. Occasionally users accidentally will put some value in these columns, and this will stop arrayformula from working. I want to protect these columns, but still allow adding/editing/deleting rows.
Consider this example spreadsheet: I want Id row to be protected, but allow addition/deletion of rows.
https://docs.google.com/spreadsheets/d/1Dnj7OE5XZL09gllHVwPgv-5GRoM-lxVCxTCI_-kURdM/edit#gid=0
Is this possible at all with Google Sheets?
You can't directly disable input but you can use Data Validation instead
By going to Data > Data Validation and filling it with the following:
Cell range: YourSheet!C2:C
Criteria: Custom formula is - =C2:C = A2:A & "["&B2:B&"]"
On invalid data: Reject input
Appeareance: Optional message
Once you've done this, try to fill some cell in the C column and you'll see a message: There was a problem - Your optional message
As a different approach you can use Apps Script Simple Triggers
By going to Tools > Script Editor and copying the following code:
function onEdit(e) {
var column = e.range.getColumn();
var sheet = SpreadsheetApp.getActiveSheet();
if (column === 3) {
e.range.setValue("");
}
}
Which is more like an "undo" function.
References
Simple Triggers
Event Objects > onEdit
rows could be added by brute force with this formula:
=ARRAYFORMULA(ROW(INDIRECT("1:"&ROWS(A:A)+1)))
but escalation cant be controlled
I am trying to create data validation dropdown from a single cell which has all , separated values and I am splitting them using SPLIT(K4,","). But when I apply the formula drop-down just goes away. Here is how it looks:
And here is where I have applied validation:
It just happens and I can't see any drop-down here. Even validation doesn't work as I type a value from given values, it still identifies it as invalid:
Here it says that it is actually not possible, but otherwise, my data column will grow very big, that's why I wanted to keep it in a single cell.
Method to reproduce: Just make a copy of this sheet and experiment on your own whatever you want it to be like:
It is not possible to do this from the sheets editor, but you could use Google Apps Script to accomplish this:
Open the script editor by selecting Tools > Script editor.
Copy and run this function:
function createDataValidation() {
const sheet = SpreadsheetApp.getActiveSheet();
const values = sheet.getRange("A1").getValue().split(","); // Get array with values from A1
const rule = SpreadsheetApp.newDataValidation().requireValueInList(values); // Create DV
sheet.getRange("F8").setDataValidation(rule); // Set DV to F8
}
Reference:
Class DataValidationBuilder
Currently I'm using the following formula to search and count the number of times a given text is used within a given cell:
=COUNTIF(Sheet1!G3:G1151, "COMPLETE")
Any ideas how I can use the same formula against multiple sheets?
Something like the following:
=COUNTIF(Sheet1, Sheet2!G3:G1151, "COMPLETE")
Thanks for your help
In case there are many sheets you want to look for, and to avoid having a to repeat the formula many times for each sheet, you can use a custom function created in Google Apps Script instead. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project:
function COUNTMANYSHEETS(sheetNames, range, text) {
sheetNames = sheetNames.split(',');
var count = 0;
sheetNames.forEach(function(sheetName) {
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var values = sheet.getRange(range).getValues();
values.forEach(function(row) {
row.forEach(function(cell) {
if (cell.indexOf(text) !== -1) count++;
});
});
});
return count;
};
Now, if you go back to your spreadsheet, you can use this function just as you would do with any other function. You just have to provide a string with all the sheet names, separated by a separator specified in the code (in this sample, a comma), another one with the range you want to look for, and the text you want to look for, as you can see here, for example:
=COUNTMANYSHEETS("Sheet1,Sheet2,Sheet3", "G3:G1151", "COMPLETE")
Notes:
It's important that you provide the sheet names separated by the separator specified in sheetNames = sheetNames.split(',');, and nothing else (not empty spaces after the comma, etc.).
It's important that you provide the range in quotes ("G3:G1151"). Otherwise, the function will interpret this as an array of values corresponding to the specified range, and you won't be able to look for the values in other sheets.
In this sample, the code looks for the string COMPLETE, and is case-sensitive. To make it case-insensitive, you could use toUpperCase() or toLowerCase().
If you wanted to look for all sheets in the spreadsheet, you could modify your function so that it only accepts the range and the text as parameters, and get all sheets via SpreadsheetApp.getActive().getSheets();.
Reference:
Custom Functions in Google Sheets
String.prototype.split()
String.prototype.indexOf()
So I click on the cell between A and 1 to select entire sheet, then I click "Format" then "Conditional Formatting" and set the rules. Basically, I have about 15 different conditions but all are in columns F through O so I use F:O. For example, if text is exactly YES change the background to green.
The issue is when I add a new row, the formatting stops for that row and the F:O rules are replaced with F1:O15, F17:O59, etc. skipping row 16.
Can I use a script that will never change when rows are added?
You can set up an onEdit trigger that applies the formatting every time you edit the sheet. I've provided an example of a function that would copy the format of cell A1 to all cells in the sheet. This link will bring you to Google's documentation for this type of work.
https://developers.google.com/apps-script/reference/spreadsheet/range
Here's the documentation on triggers...
https://developers.google.com/apps-script/guides/triggers/
function formatRange(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getRange("A1");
range.copyFormatToRange(sheet,1,sheet.getLastColumn(),1,sheet.getLastRow())
}