Data Validation in code (Google Sheets) - google-sheets

I have the following script that runs on the 'onEdit' trigger (that part is working just fine)
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var myRange = SpreadsheetApp.getActiveRange();
if(sheet.getName() == "Plan" && myRange.getColumn() == 2 && myRange.getRow() > 3){
var option = new Array();
option[0]="0";
option[1]="1";
option[2]="2";
var dv = sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).getValidation();
dv.setAllowInvalidData(false);
dv.setHelpText("Some help text here");
dv.setCriteria(SpreadsheetApp.DataValidationCriteria.ITEM_IN_LIST,true,option );
sheet.getRange(myRange.getRow(),myRange.getColumn() + 1) .setValidation(dv);
}
}
That most of the code came from the answer on this question. Problem is that the
dv.setCriteria(SpreadsheetApp.DataValidationCriteria.ITEM_IN_LIST,true,option );
line of code doesn't work, the compiler won't even let me save it. In my looking around as to why, it seems Google has changed how it is handled, and taken their docs about it offline. Can anyone help me get this working?

yep it seems something changed...
try that:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var myRange = SpreadsheetApp.getActiveRange();
if(sheet.getName() == "Plan" && myRange.getColumn() == 2 && myRange.getRow() > 3){
var option = new Array();
option[0]="0";
option[1]="1";
option[2]="2";
// var dv = sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).getValidation();
var dv = sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).getDataValidation();
var dv = SpreadsheetApp.newDataValidation();
// dv.setAllowInvalidData(false);
dv.setAllowInvalid(false);
dv.setHelpText("Some help text here");
dv.requireValueInList(option, true);
// dv.setCriteria(SpreadsheetApp.DataValidationCriteria.ITEM_IN_LIST,true,option );
// sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).setValidation(dv);
sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).setDataValidation(dv.build());
}
}

Related

Jump to a specific cell based on Active Cell

I'm completely new to Java Scipt although over the years I've messed around with VBA & Macros in Excel. I am now using Google Sheets almost exclusively, hence needing to learn Java.
I'm trying to jump to a specific cell (E5) if the current cell is (C14). This is what I've put together so far using scripts 'borrrowed' from others.
ie On entry of data in Cell C13 and pressing Enter, focus goes to Cell C14. The next data is to go into Cell E5.
function onSelectionChange(e) {
var sheetNames = ["Score Input"]; // Set the sheet name.
var ranges = ["C14"]; // Set the range 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) {
jumpToDetails();
}
};
function jumpToDetails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Score Input");
var goToRange = sheet.getRange('c14').getValue();
//sheet.getRange(goToRange).activate();
SpreadsheetApp.getActive().getRange('E5').activate();
}
It worked, or did until I inserted a row in the sheet, and even though I have changed the associated cell addresses, it now doesn't work?
Two questions. 'Why has it stopped working'? and 'Is there a simpler way to do it'?
I prefer using onEdit(e) on range C13 and jump to E5
For instance here is a script that jump from one cell to the following in addresses'list
function onEdit(event){
var sh = event.source.getActiveSheet();
var rng = event.source.getActiveRange();
if (sh.getName() == 'Score Input'){ // adapt
var addresses = ["C13","E5","E10","H10","E13","H13","E16"]; // adapt
var values = addresses.join().split(",");
var item = values.indexOf(rng.getA1Notation());
if (item < addresses.length - 1){
sh.setActiveSelection(addresses[item + 1]); // except last one
}
}
}
if you want to be able to add rows and columns, play with named ranges (for instance ranges names 'first', 'second, 'third')
function onEdit(event){
var sh = event.source.getActiveSheet();
var rng = event.source.getActiveRange();
if (sh.getName() == 'Score Input'){
var addresses = ["first","second","third"];
var values = addresses
.map(ad => myGetRangeByName(ad))
.join().split(",");
var item = values.indexOf(rng.getA1Notation());
if (item < addresses.length - 1){
sh.setActiveSelection(addresses[item + 1]); // except last one
}
}
}
function myGetRangeByName(n) {
return SpreadsheetApp.getActiveSpreadsheet().getRangeByName(n).getA1Notation();
}
reference
class NamedRange

am getting a reference Error : event is not defined onEdit I do not understand why I am getting this error>?

I am trying to pull from one sheet to the next sheet.
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();`enter code here`
if(s.getName() == "Main" && r.getColumn() == 2 && r.getValue() == "Chair") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Chair");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
The code above comes from this Github Gist from scottsommer22.
Do not run the function in the script editor. It runs automatically when you hand edit the spreadsheet, and the event object will then be filled in the way the function needs. See simple triggers.
According to an answer from Google Apps onEdit Event - event.source is undefined for, these built-in simple event handlers need the context of the event to be passed in in order to work properly. If you run them from the Script Editor directly, then no event object is passed in, which is why you get the error.
If you want to run and test your code on the script editor and at the same time be able to use the onEdit trigger, you can edit your script to something like this:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var r = SpreadsheetApp.getActiveSpreadsheet().getActiveRange();
if(s.getName() == "Main" && r.getColumn() == 2 && r.getValue() == "Chair") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Chair");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}

Copy Row of data based on two conditions to a sheet which is opened by ID or URL

I have a code that helps me copy a row of data based on a condition in a given column. I have a sheet called "Master" which has around 1000 rows of data. I want to move a row of data to a sheet called "Master Responses" if column 1 of "Master" has the word "Positive" or "Negative" in it. I used the or function (||) in the IF STATEMENT to select the condition (that is if "Positive" is entered or "Negative") but the row is only copied when I type "Positive" in the first column. When I type "Negative" in the first column the row is not copied. Also, I wanted to know how the code should be modified if I had to call the "Master Responses" sheet by using ".openByID or .openByURL". I have attached the code, please feel free to edit it. I am new to scripting and have been stuck on this for over a month. Any help would be appreciated. Thanks in advance.
function onEdit()
{
var sheetNamesToWatch = ["Master"];
var columnNumberToWatch = 1;
var valuesToWatch = ["Positive"];
var valuesToWatch1 = ["Negative"];
var targetSheetsToMoveTheRowTo = ["Master Responses"];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveCell();
if(sheetNamesToWatch.indexOf(sheet.getName()) != -1 &&
valuesToWatch.indexOf(range.getValue()) != -1 ||
valuesToWatch1.indexOf(range.getValue()) != -1 && range.getColumn()
==columnNumberToWatch)
{
var targetSheet = ss.getSheetByName(targetSheetsToMoveTheRowTo[valuesToWatch.indexOf(range.getValue())]);
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(range.getRow(), 1, 1,
sheet.getLastColumn()).copyTo(targetRange);
}
}
I'm not sure about .openByID or .openByURL, however this should fix the problem with "Negative".
I also added extra parenthesis on the first if statement so that it was clear to me what it was checking on.
function onEdit()
{
var sheetNamesToWatch = ["Master"];
var columnNumberToWatch = 1;
var valuesToWatch = ["Positive"];
var valuesToWatch1 = ["Negative"];
var targetSheetsToMoveTheRowTo = ["Master Responses"];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveCell();
if((sheetNamesToWatch.indexOf(sheet.getName()) != -1) && ((valuesToWatch.indexOf(range.getValue()) != -1) ||
(valuesToWatch1.indexOf(range.getValue()) != -1)) && (range.getColumn() ==columnNumberToWatch))
{
if (valuesToWatch.indexOf(range.getValue()) != -1)
{
var targetSheet = ss.getSheetByName(targetSheetsToMoveTheRowTo[valuesToWatch.indexOf(range.getValue())]);
}
else
{
var targetSheet = ss.getSheetByName(targetSheetsToMoveTheRowTo[valuesToWatch1.indexOf(range.getValue())]);
}
var targetRange = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
sheet.getRange(range.getRow(), 1, 1,
sheet.getLastColumn()).copyTo(targetRange);
}
}

Automatic hiding columns based on cell value in google spreadsheets

I have too many columns in the sheet and so many columns to hide as well, but while executing the script it runs half way and stopped saying maximum time reached.
When I again tried to execute it stopped exactly where I stopped previously. So I would like to have some customization that if the column is already hidden can skip that column and work on the others.
Is there any way to do it.
Here is the code I used:
function hideColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('ANALYTIC');
var data = sh.getRange('6:6').getValues();
var numCols = sh.getMaxColumns();
var numRows = sh.getMaxRows();
for(var i = 0; i <= numCols; i++){
if(data[0][i] == ""){
sh.hideColumns(i+1);
} else {
sh.unhideColumn(sh.getRange(1, i+1, numRows, 1));
}
}
}
Please help me.
You can use the documentProperties to store the last column before the end of the execution. To prevent the run from stopping abruptly you stop the run a little prematurely at 5min (execution will terminate at 6min mark) mark and store the column number in the documentProperty. You also display an alert asking you rerun the script.
Then retrieve the column number on the next run and start from there. If the program gets through the complete loop you delete the said properties. So you start from zero if you rerun the script next time.
Below is the code for the same
function hideColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('ANALYTIC');
var data = sh.getRange('6:6').getValues();
var numCols = sh.getMaxColumns();
var numRows = sh.getMaxRows();
var docProp = PropertiesService.getDocumentProperties()
var startCol = Number(docProp.getProperty("startCol")) //if there is no propert called startCol will return Null, Number(Null) = 0
Logger.log(startCol)
var startTime = new Date().getTime()
var ms5min = 5*60*1000 //5min in millseconds
for(var i = startCol; i <= numCols; i++){
if(data[0][i] == ""){
sh.hideColumns(i+1);
} else {
sh.unhideColumn(sh.getRange(1, i+1, numRows, 1));
}
var curTime = new Date().getTime()
var elasped = curTime-startTime
if (elasped >= ms5min){
docProp.setProperty("startCol", i)
SpreadsheetApp.getUi().alert("Please restart Run, exceeded 5min mark")
return
}
}
Logger.log(elasped)
docProp.deleteAllProperties()
}
If your sheet has under 10,000 columns (PropertiesService limit) you can use this script:
function hideColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('ANALYTIC');
var data = sh.getRange('6:6').getValues();
var documentProperties = PropertiesService.getDocumentProperties()
var documentPropertiesVals = documentProperties.getProperties();
var numCols = sh.getMaxColumns();
for(var i = 0; i < numCols; i++){
if (!(cPlusInt(i) in documentPropertiesVals)) {
documentPropertiesVals[cPlusInt(i)] === 'empty';
}
if (documentPropertiesVals[cPlusInt(i)] === 'hidden' && data[0][i] == "") continue;
if (documentPropertiesVals[cPlusInt(i)] === 'unhidden' && data[0][i] != "") continue;
if(data[0][i] == ""){
sh.hideColumns(i+1);
documentProperties.setProperty(cPlusInt(i), 'hidden')
} else {
sh.unhideColumn(sh.getRange(1, i+1, 1, 1));
documentProperties.setProperty(cPlusInt(i), 'unhidden')
}
}
}
function cPlusInt(num) {
return 'c'+num.toString()
}
You at first you may need to run this few times (many write operation to PropertiesService may be sluggish) but later it's "blazingly" fast (0.1 s per new column).
Better answer using #Jack Brown's idea
It is possible to make single save to PropertiesService - you would need to incorporate time limit from #Jack Brown's answer, this way:
function hideColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('ANALYTIC');
var data = sh.getRange('6:6').getValues();
var documentProperties = PropertiesService.getDocumentProperties()
var documentPropertiesVals = documentProperties.getProperties();
var numCols = sh.getMaxColumns();
var startTime = new Date().getTime()
var ms5min = 5*60*1000 //5min in millseconds
for(var i = 0; i < numCols; i++){
if (!(cPlusInt(i) in documentPropertiesVals)) {
documentPropertiesVals[cPlusInt(i)] === 'empty';
}
if (documentPropertiesVals[cPlusInt(i)] === 'hidden' && data[0][i] == "") continue;
if (documentPropertiesVals[cPlusInt(i)] === 'unhidden' && data[0][i] != "") continue;
if(data[0][i] == ""){
sh.hideColumns(i+1);
documentPropertiesVals[cPlusInt(i)] = 'hidden'
} else {
sh.unhideColumn(sh.getRange(1, i+1, 1, 1));
documentPropertiesVals[cPlusInt(i)] = 'unhidden'
}
var curTime = new Date().getTime()
var elapsed = curTime-startTime
if (elapsed >= ms5min){
break;
}
}
documentProperties.setProperties(documentPropertiesVals)
if (elapsed >= ms5min){
SpreadsheetApp.getUi().alert("Please restart Run, exceeded 5min mark")
}
}
cPlusInt function explanation
cPlusInt is necessary because of weird problems with google's PropertiesService. Object gotten from PropertiesService returns undefined at integer keys. To see problem use this code:
function test() {
var obj = {};
for (var i=0; i<10; i++) {
obj[i.toString()] = 'aa' + i.toString();
}
var documentProperties = PropertiesService.getScriptProperties();
documentProperties.deleteAllProperties();
documentProperties.setProperties(obj);
obj = documentProperties.getProperties();
Logger.log(obj)
for (var i in obj) {
Logger.log('%s %s %s', i, obj[i], i === '1');
}
}

making script applicable to only 1 specific google worksheet

I created this script to apply to only the worksheet with the name "jan" -- however it runs on all the worksheets no matter how I try. Who can help me check what I am doing wrong?
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("jan");
var r = sheet.getActiveCell();
if( r.getColumn() == 3 ) {
var nextCell = r.offset(0, 1);
if( nextCell.getValue() === '' )
var time = new Date();
time = Utilities.formatDate(time, "GMT", "dd/MM");
nextCell.setValue(time);
};
};
Try:
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if (sheet.getName() == "jan") {
// DO SOMETHING TO "jan"
Logger.log("working on sheet jan...");
}
}
To put the above with what you are doing:
function onEdit(e) {
var activeSheet = e.source.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("jan");
var r = sheet.getActiveCell();
if( r.getColumn() == 3 && activeSheet.getName()=="jan") {
var nextCell = r.offset(0, 1);
if( nextCell.getValue() === '' )
var time = new Date();
time = Utilities.formatDate(time, "GMT", "dd/MM");
nextCell.setValue(time);
};
};

Resources