function createSlide() {
try {
let spread = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spread.getSheetByName("Feuille 1");
let values = sheet.getDataRange().getValues();
values.shift(); // remove header
let update = false;
values.forEach( row => {
// if checked row[4] is true or link is present
if( ( row[20] == true ) || ( row[21] == "" ) ) {
// open the template
//SpreadsheetApp.getActiveSpreadsheet().toast("Génération du GSlides en cours...⌛");
let presentation = SlidesApp.openById("1kZ4zCaInca68peo4uKX0MEFLnWuyOdrFz9aRbb1fvIw");
let slides = presentation.getSlides();
// create a new presentation named for the employee
presentation = SlidesApp.create("Alvo - BR - " + row[0]);
// copy the template slides to the new presentation
slides.forEach( (slide,index) => {
presentation.insertSlide(index,slide);
}
);
let newSlides = presentation.getSlides();
newSlides[1].remove();
// there is only one slide
slides = presentation.getSlides()[0];
//slides.replaceAllText("{{Vendor}}",row[0]);
slides.replaceAllText("{{Description}}",row[15]);
//slides.replaceAllText("{{Order#}}",row[2]);
//slides.replaceAllText("{{Type}}",row[3]);
//slides.replaceAllText("{date of hire}",Utilities.formatDate(row[3],"PST","dd/MM/yyyy"));
// update the values
row[20] = false;
row[21] = presentation.getUrl();
update = true;
}
}
);
// update the spreadsheet
if( update ) {
values = values.map( row => row.slice(20,22) );
sheet.getRange(2,21,values.length,2).setValues(values);
}
}
catch(err) {
console.log(err);
}
}
function onEdit(e) {
// check if the edited cell is in column 5
if (e.range.getColumn() == 20) {
// check if the value of the edited cell is true
if (e.value == true) {
// call the createSlide function
createSlide();
}
}
}
Here is my Apps Script code.
The fact is that I would like that my createSlide() function, being executed when a checkbox is checked.
I also want the function to be executed only for the row where the checkbox is checked.
Tried further things, but actually, if I made any changes on the spreadsheet, a Google Slide is generated, and that's not what I want 😅
Try something like this:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == "Your sheet name" && e.range.columnStart == 20 && e.value == "TRUE") {
createSlide();
}
}
function createSlide(e) {
try {
let ss = SpreadsheetApp.getActive();
let sh = ss.getSheetByName("Feuille 1");
let values = sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues();
let update = false;
values.forEach( row => {
if( ( row[20] == true ) || ( row[21] == "" ) ) {
let presentation = SlidesApp.openById("1kZ4zCaInca68peo4uKX0MEFLnWuyOdrFz9aRbb1fvIw");
let slides = presentation.getSlides();
presentation = SlidesApp.create("Alvo - BR - " + row[0]);
slides.forEach( (slide,index) => {
presentation.insertSlide(index,slide);
}
);
let newSlides = presentation.getSlides();
newSlides[1].remove();
slides = presentation.getSlides()[0];
slides.replaceAllText("{{Description}}",row[15]);
row[20] = false;
row[21] = presentation.getUrl();
update = true;
}
}
);
if( update ) {
values = values.map( row => row.slice(20,22) );
sh.getRange(2,21,values.length,2).setValues(values);
}
}
catch(err) {
console.log(err);
}
}
Related
I have data sheet as below-
I am trying to check for values in column Count of deletions if it is empty or not a number to flag an error at an account level,
because there will be only one entry of Count of deletions which will be at the first row entry of Account ID (unique) as illustrated in the pic above.
I did this to just validate the column Count of deletions
function myFunction() {
SS = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SS.getSheetByName("Sheet1");
var Validation_sheet = SS.getSheetByName("Sheet2");
var last_row = sheet.getLastRow();
var values = sheet.getRange(2,5,last_row).getValues();
var Status_values = sheet.getRange(2,4,last_row).getValues();
var result = isnumberorEmpty(values, Status_values);
const Count_setvalue = Validation_sheet.getRange("A2");
var errors_c = [];
for (key in result){
var check1 = result[key] == "empty";
var check2 = result[key] == "Not a number";
if(check1 == true || check2 == true){
errors_c.push(key);
}
}
if (errors_c.length > 0){
Count_setvalue.setValue("Row(s) " + errors_c.join(" and ") + " have error");
}
else{
Count_setvalue.setValue("No errors");
}
}
function isnumberorEmpty(array, array1) {
let result = {};
for (let i = 0; i < array.length-1; i++) {
if( array1[i] == "Completed"){
let row = array[i];
if (row[0] === "") {
result[i+2] = "empty";
}
else if (row[0] === "Ignored") {
result[i+2] = "No error";
}
else if (!isNaN(row[0])) {
result[i+2] = "number";
}
else {
result[i+2] = "Not a number";
}
}
else{
}
}
return result;
}
The above checks for column Count of deletions and validates only where corresponding rows in column Status has Completed.
The output will show me error for rows, 8 and 10-18,
but it should not as the values are entered at the account level.
However, there should be error for only Account4 and Account5, meaning the error should be only for Row 16 and row 18 as below -
From your updated question, I thought that the reason for your current issue might be due to that you checked columns "D" and "E". In order to achieve However, there should be error for only Account4 and Account5, meaning the error should be only for Row 16 and row 18 as below -, how about checking the columns "A", "D" and "E"? In this case, how about the following sample script?
Sample script 1:
function myFunction1() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Process in the source sheet.
const srcSheet = ss.getSheetByName("Sheet1");
const values = srcSheet.getRange("A2:E" + srcSheet.getLastRow()).getDisplayValues();
const { errors_c } = values.reduce((o, r, i) => {
if (i == 0 || o.temp != r[0]) {
o.temp = r[0];
if (r[3] == "Completed" && !r[4]) {
o.errors_c.push(i + 2);
}
}
return o;
}, { errors_c: [], temp: "" });
// Process in the destination sheet.
const dstRange = ss.getSheetByName("Sheet2").getRange("A2");
if (errors_c.length > 0) {
dstRange.setValue("Row(s) " + errors_c.join(" and ") + " have error");
} else {
dstRange.setValue("No errors");
}
}
In this modification, the columns "A", "D" and "E" are checked.
When this script is tested to your sample input Spreadsheet, Row(s) 16 and 18 have error is put to the cell "A2" of "Sheet2".
Sample script 2:
Although unfortunately, I'm not sure whether I could correctly understand your expected result, if you want to check all values of column "D" for each "Account ID", how about the following sample script?
function myFunctionc2() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Process in the source sheet.
const srcSheet = ss.getSheetByName("Sheet1");
const values = srcSheet.getRange("A2:E" + srcSheet.getLastRow()).getDisplayValues();
const obj = values.reduce((m, r, i) => m.set(r[0], m.has(r[0]) ? { v: [...m.get(r[0]).v, r[3]], i: m.get(r[0]).i } : { v: [r[3]], i: i + 2 }), new Map()).values();
const errors_c = [...obj].reduce((ar, { v, i }) => {
if (v.every(e => e == "Completed") && !values[i - 2][4]) {
ar.push(i);
}
return ar;
}, []);
// Process in the destination sheet.
const dstRange = ss.getSheetByName("Sheet2").getRange("A2");
if (errors_c.length > 0) {
dstRange.setValue("Row(s) " + errors_c.join(" and ") + " have error");
} else {
dstRange.setValue("No errors");
}
}
Reference:
reduce()
I am trying to toggle a cell value in 8 sheets of a spreadsheet basically to highlight the cell value and get the reader's attention
I have an onEdit function with other functions,
function onEdit(){
cp = SpreadsheetApp.getActiveSpreadsheet();
// The fucntion for hide/unhide sheets/tabs
var sheet1 = cp.getSheetByName("1 s");
var sheet2 = cp.getSheetByName("2 s");
var sheet3 = cp.getSheetByName("3 s");
var sheet4 = cp.getSheetByName("4 s");
var sheet5 = cp.getSheetByName("5 s");
var sheet6 = cp.getSheetByName("6 s");
var sheet7 = cp.getSheetByName("7 s");
var sheet8 = cp.getSheetByName("8 s");
var Overview = cp.getSheetByName("Overview");
var [cella, cellb, cellc, celld, celle, cellf, cellg, cellh, celli] = Overview.getRange("C6:C14").getValues().flat();
if (cella =="Yes") { sheet1.showSheet(); } else { sheet1.hideSheet(); }
if (cellb == "Yes") { sheet2.showSheet(); } else { sheet2.hideSheet(); }
if (cellc == "Yes") { sheet3.showSheet(); } else { sheet3.hideSheet(); }
if (celld == "Yes") { sheet4.showSheet(); } else { sheet4.hideSheet(); }
if (celle == "Yes") { sheet5.showSheet(); } else { sheet5.hideSheet(); }
if (cellf == "Yes") { sheet6.showSheet(); } else { sheet6.hideSheet(); }
if (cellg == "Yes") { sheet7.showSheet(); } else { sheet7.hideSheet(); }
if (celli == "Yes") { sheet8.showSheet(); } else { sheet8.hideSheet(); }
// The fucntion for hide/unhide row in 8 s sheet
var eos = cp.getSheetByName("8 s");
var cellx = eos.getRange("D13").getValue();
if ((cellx == "E") || ((cellx == ""))) { eos.hideRows(15); } else { eos.showRows(15); }
// The fucntion for hide/unhide rows in 1 s
var general_info = cp.getSheetByName("0 S");
var check = general_info.getRange("J9").getValue();
if (check == "Dir") { sheet1.hideRows(37, 8); } else {
if ((check == "Par") || (check == "")) {
sheet1.showRows(37, 8);
}
}
// script for toggle
var toggle = sheet8.getRange("G2");
for(var i=0;i<50;i++) {
if( i%2 == 0 ){
toggle.setFontColor("red");
toggle.setFontSize(12);
}
else{
toggle.setFontColor("black");
toggle.setFontSize(10);
}
}
SpreadsheetApp.flush();
Utilities.sleep(100);
}
The script for toggle code block uses a for loop and changes the font color and size to grab attention.
Only the else part of the code block works upon edit and then it stops.
Is it because there are other code blocks of hiding/unhiding sheets and hiding/unhiding rows the toggle does not work.
I am not understanding why the cell does not toggle. Please help
From your following reply,
its cell G8. The goal is to toggle the font size and color of the text value which is "Please fill all details in this sheet" in cell G8 in sheet8, This increase and decrease along with change of color will grab the attention of the onlooker
Is your expected result as follows? Please modify your script as follows.
From:
var toggle = sheet8.getRange("G2");
for(var i=0;i<50;i++) {
if( i%2 == 0 ){
toggle.setFontColor("red");
toggle.setFontSize(12);
}
else{
toggle.setFontColor("black");
toggle.setFontSize(10);
}
}
SpreadsheetApp.flush();
Utilities.sleep(100);
To:
var toggle = sheet8.getRange("G8").activate(); // Modified
for (var i = 0; i < 50; i++) {
if (i % 2 == 0) {
toggle.setFontColor("red");
toggle.setFontSize(12);
} else {
toggle.setFontColor("black");
toggle.setFontSize(10);
}
SpreadsheetApp.flush(); // Modified
Utilities.sleep(100); // Modified
}
Our company works at different properties repairing appliances, I would like to build a database to search up the information on each appliance at specific properties and in their specific apt/units, I created a form to start this process, but I need help with some complex coding.
I first created a box for the property, then I created an "Apt/Unit" box. The idea is when I select a property, the units tied to that property are shown in dropdown/type searchable list in the Apt/Unit box.
I then created an "Appliance type" box. The idea is when the "Apt/Unit" is selected, it will display the dropdown/type searchable list of the appliances tied to that specific "Apt/Unit".
Then I created boxes for the info for the appliance (Brand, Model #, Serial #, & Color), this is a bit more self-explanatory - once the appliance type is selected, it will display the respective information for each box for that appliance.
Here's the link to the Google sheet: https://docs.google.com/spreadsheets/d/1JZhEYjk5xVN3uOc_Ucb8HFr6d96XQ2Q_ehAd-d_o0ME/edit?usp=sharing
Any help is appreciated!
non-scripted solution:
=IFERROR({INDEX(IFERROR(Data!A1:G1/0)); Data!A1:G1; QUERY({Data!A2:G}, "where 1=1 "&
IF(C10="",,"and lower(Col1) contains '"&LOWER(C10)&"'")&
IF(C12="",,"and Col2 = "&C12)&
IF(C14="",,"and lower(Col3) contains '"&LOWER(C14)&"'")&
IF(C16="",,"and lower(Col4) contains '"&LOWER(C16)&"'")&
IF(C18="",,"and lower(Col5) contains '"&LOWER(C18)&"'")&
IF(C20="",,"and lower(Col6) contains '"&LOWER(C20)&"'")&
IF(C22="",,"and lower(Col7) contains '"&LOWER(C22)&"'"), 0)}, {"";"no data"})
demo sheet
Here is third variant of the script:
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
function onLoad() { reset() }
function reset() {
SS.toast('Please wait...');
SHEET_USERFACE.getRange('c9:c21').clearContent();
SHEET_USERFACE.getRange('c9:c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_prop(obj);
update_menu_unit(obj);
update_menu_type(obj);
SS.toast('The sheet has been reset');
}
function onEdit(e) {
if (e.range.getSheet().getName() != 'Userface') return;
if (e.range.columnStart != 3) return;
// Property menu
if (e.range.rowStart == 9) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c11:c21').clearContent();
SHEET_USERFACE.getRange('c11:c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_unit(obj);
update_menu_type(obj);
e.source.toast('The sheet has been updated');
}
// Apt/Unit menu
if (e.range.rowStart == 11) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c13:c21').clearContent();
SHEET_USERFACE.getRange('c13').clearDataValidations();
var obj = make_obj_from_data();
update_menu_type(obj);
e.source.toast('The sheet has been updated');
}
// Applicance type menu
if (e.range.rowStart == 13) {
e.source.toast('Please, wait...');
SHEET_USERFACE.getRange('c15:c21').clearContent();
var obj = make_obj_from_data();
update_brand_model_serial_color(obj);
e.source.toast('The sheet has been updated');
}
}
function make_obj_from_data() {
var data = SHEET_DATA.getDataRange().getValues().slice(1);
var obj = {};
for (let row of data) {
var [prop, unit, type, ...etc] = row;
try {
obj[prop][unit][type] = etc;
}
catch(e) {
try {
obj[prop][unit] = {}; obj[prop][unit][type] = etc;
}
catch(e) {
obj[prop] = {}; obj[prop][unit] = {}; obj[prop][unit][type] = etc;
}
}
}
return obj;
}
function update_menu_prop(obj) {
var cell = SHEET_USERFACE.getRange('c9');
try {
var list = Object.keys(obj);
set_data_validation(cell, list);
} catch(e) {
console.log('update_menu_prop(obj)');
console.log(e);
}
}
function update_menu_unit(obj) {
var prop = SHEET_USERFACE.getRange('c9').getValue();
var cell = SHEET_USERFACE.getRange('c11');
try {
var list = Object.keys(obj[prop]);
set_data_validation(cell, list);
} catch(e) {
console.log('update_menu_unit(obj)');
console.log(e);
}
}
function update_menu_type(obj) {
var prop = SHEET_USERFACE.getRange('c9').getValue();
var unit = SHEET_USERFACE.getRange('c11').getValue();
var cell = SHEET_USERFACE.getRange('c13');
try {
var list = Object.keys(obj[prop][unit]);
set_data_validation(cell, list);
if (list.length == 1) update_brand_model_serial_color(obj)
} catch(e) {
console.log('update_menu_type(obj)');
console.log(e);
}
}
function update_brand_model_serial_color(obj) {
var [prop,,unit,,type] = SHEET_USERFACE.getRange('c9:c13').getValues();
try {
var [brand, model, serial, color] = obj[prop][unit][type];
var arr = [[brand],[''],[model],[''],[serial],[''],[color]];
SHEET_USERFACE.getRange('c15:c21').setValues(arr);
} catch(e) {
console.log('update_brand_model_serial_color(obj)');
console.log(e);
}
}
function set_data_validation(cell, list) {
var rule = SpreadsheetApp.newDataValidation().requireValueInList(list).build();
cell.setDataValidation(rule);
// put the value in the cell if there is just one element in the list
if (list.length == 1) cell.setValue(list[0]);
}
Here is my sheet.
It works about that way as it does any similar interface. You select the first menu and it changes data validation for the second menu and cleans the third menu. Then you select the second menu and it changes the third one. As soon as you change the third menu it fills the rest fields.
Since you're using just the three menus and they supposed to be changed step by step I decided to 'hardcode' them. It's not the best practice and there can be problems if/when you decide to change the functionality. But for this particular case I think the 'hardcoding' is forgivable. It works relatively fast and the code is relatively readable.
Just for fun I've made it. But this is overkill:
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
function onLoad() { reset() }
function onEdit(e) {
if (e.range.getSheet().getName() != 'Userface') return;
if (e.range.columnStart != 3) return;
if (![9,11,13,15,17,19,21].includes(e.range.rowStart)) return;
e.source.toast('Please, wait...');
set_filter(e.range.offset(0,-1).getValue(), e.value);
set_all_menus();
e.source.toast('The sheet has been updated');
}
function reset() {
SS.toast('Please wait...');
try { SHEET_DATA.getFilter().remove() } catch(e) {}
SHEET_USERFACE.getRange('c9:c21').clearContent().clearDataValidations();
set_all_menus();
SS.toast('The sheet has been updated');
}
function set_all_menus() {
var data = SHEET_DATA.getDataRange().getDisplayValues().filter((_,i) => !SHEET_DATA.isRowHiddenByFilter(i+1));
set_menu(data, 'b9', 'c9');
set_menu(data, 'b11', 'c11');
set_menu(data, 'b13', 'c13');
set_menu(data, 'b15', 'c15');
set_menu(data, 'b17', 'c17');
set_menu(data, 'b19', 'c19');
set_menu(data, 'b21', 'c21');
}
function set_menu(data, title, cell) {
var menu_title = SHEET_USERFACE.getRange(title).getValue();
var menu_cell = SHEET_USERFACE.getRange(cell);
var col_index = data[0].indexOf(menu_title);
var menu_list = [...new Set([...data.map(e => e[col_index])])].slice(1);
var menu_rule = SpreadsheetApp.newDataValidation().requireValueInList(menu_list).build();
menu_cell.setDataValidation(menu_rule);
}
function set_filter(column_title, value) {
// get all the data and col index
var [header, ...data] = SHEET_DATA.getDataRange().getValues();
var col_index = header.indexOf(column_title);
// unhide all values of the given column
var clear = SpreadsheetApp.newFilterCriteria().setHiddenValues([]).build();
var range = SHEET_DATA.getDataRange();
var filter = range.getFilter() || range.createFilter()
filter.setColumnFilterCriteria(col_index+1, clear);
// get the values to hide
var col_data = data.map(e => e[col_index]);
var filtered = col_data.filter( (e, i) => e != value && SHEET_DATA.isRowHiddenByFilter(i+1) );
var to_hide = col_data.filter( e => e != value );
var hidden = [...new Set([...filtered, ...to_hide])];
// hide the values with the filter
var criteria = SpreadsheetApp.newFilterCriteria().setHiddenValues(hidden).build();
var range = SHEET_DATA.getDataRange();
var filter = range.getFilter() || range.createFilter()
filter.setColumnFilterCriteria(col_index+1, criteria);
}
Here is the sheet.
It works quite slow. I'd propose to use the native filters instead. Basically the script turns on and off the filters an changes data validation for the dropdown menus respectively.
Update
Here another version of the script. It works much faster but it uses the 'helper sheet' to store temporary data (the filtered table). You can hide the 'helper sheet' if you want.
// global variables
var SS = SpreadsheetApp.getActiveSpreadsheet();
var SHEET_USERFACE = SS.getSheetByName('Userface');
var SHEET_DATA = SS.getSheetByName('Data');
var SHEET_HELPER = SS.getSheetByName('Helper'); // the hidden sheet with temp data
var PROPERTY_LIST = [...new Set(SHEET_DATA.getRange('a2:a').getValues().flat())]; // 'Property' list
var DATA_OBJ = {};
function onLoad() { reset() }
function onEdit(e) {
var {range, source, value} = e;
if (range.getSheet().getName() != 'Userface') return;
if (range.columnStart != 3) return;
if (![9,11,13,15,17,19,21].includes(range.rowStart)) return;
source.toast('Please, wait...');
// reset whenever the first menu is changing
if (range.rowStart == 9) {
reset();
source.getRange('c9').setValue(value);
}
var col_header = range.offset(0,-1).getValue();
update_sheet_helper(col_header, value);
update_all_dropdown_menus();
source.toast('The sheet has been updated');
}
function reset() {
SS.toast('Please wait...');
// copy data from SHEET_DATA to SHEET_HELPER
SHEET_USERFACE.getRange('c9:c21').clearContent().clearDataValidations();
SHEET_DATA.getDataRange().copyTo(SHEET_HELPER.clearContents().getRange(1,1));
update_data_obj();
update_all_dropdown_menus();
SS.toast('The sheet has been updated');
}
// make DATA_OBJECT from SHEET_HELPER
function update_data_obj() {
DATA_OBJ = {};
var [header, ...data] = SHEET_HELPER.getDataRange().getValues();
for (let i in header) DATA_OBJ[header[i]] = data.map(e => e[i]);
DATA_OBJ['Property'] = PROPERTY_LIST; // let 'Property' list will be full always
}
// remove from SHEET_DATA_HELPER all the rows
// that have no given value in column with given title
function update_sheet_helper(col_title, value) {
var [header, ...data] = SHEET_HELPER.getDataRange().getValues();
var col_index = header.indexOf(col_title);
data = data.filter(k => k[col_index] == value);
var table = [header, ...data];
SHEET_HELPER.clearContents().getRange(1,1,table.length, table[0].length).setValues(table);
update_data_obj();
}
function update_all_dropdown_menus() {
SHEET_USERFACE.getRange('b9:c21').getValues().forEach((row,i) => {
if (row[0] != '') set_data_validation(DATA_OBJ[row[0]], 'c' + (i+9));
});
function set_data_validation(data, cell_address) {
var menu_list = [...new Set([...data])]; // remove duplicates from the array
var menu_rule = SpreadsheetApp.newDataValidation().requireValueInList(menu_list).build();
var cell_range = SHEET_USERFACE.getRange(cell_address)
cell_range.setDataValidation(menu_rule);
if (menu_list.length == 1) cell_range.setValue(menu_list[0]);
}
}
The sheet is here.
I am using the below code to create the validations in google sheet (contributed by Cooper), what this script does is it automatically check the applicable headers and create the dropdown with values and hide the columns which are not applicable.
I am trying to solve here is:
The script checks the applicable headers related to the Product Selection
It creates the dropdown with validation values
Instead of hiding the not applicable columns, It removes them from the sheet
I am a beginner to google script and tried using the deletecolumn function but unable to get it work.
Please help me out here.
function loadObjectsAndCreateProductDropDown() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const psh = ss.getSheetByName('Sheet1');
const [ph, ...prds] = sh.getRange(1, 1, 10, 6).getValues().filter(r => r[0]);
const [ch, ...chcs] = sh.getRange(11, 1, 10, 10).getValues().filter(r => r.join());
let pidx = {};
ph.forEach((h, i) => { pidx[h] = i });
let prd = { pA: [] };
prds.forEach(r => {
if (!prd.hasOwnProperty(r[0])) {
prd[r[0]] = { type: r[pidx['Type']], size: r[pidx['Size']], color: r[pidx['Color']], material: r[pidx['Material']], length: r[pidx['Length']] };
prd.pA.push(r[0]);
}
});
let cidx = {};
let chc = {};
ch.forEach((h, i) => { cidx[h] = i; chc[h] = [] });
chcs.forEach(r => {
r.forEach((c, i) => {
if (c && c.length > 0) chc[ch[i]].push(c)
})
})
const ps = PropertiesService.getScriptProperties();
ps.setProperty('product_matrix', JSON.stringify(prd));
ps.setProperty('product_choices', JSON.stringify(chc));
Logger.log(ps.getProperty('product_matrix'));
Logger.log(ps.getProperty('product_choices'));
psh.getRange('A2').setDataValidation(SpreadsheetApp.newDataValidation().requireValueInList(prd.pA).build());
}
//I chose to use an installable dropdown. I'm not sure if it's needed. Its up to you.
function onMyEdit(e) {
//e.source.toast('entry')
const sh = e.range.getSheet();
if (sh.getName() == 'Sheet1' && e.range.columnStart == 1 && e.range.rowStart == 2 && e.value) {
//e.source.toast('flag1');
sh.getRange('C2:G2').clearDataValidations();
let ps = PropertiesService.getScriptProperties();
let prodObj = JSON.parse(ps.getProperty('product_matrix'));//recovering objects from PropertiesService
let choiObj = JSON.parse(ps.getProperty('product_choices'));
let hA = sh.getRange(1, 1, 1, sh.getLastColumn()).getDisplayValues().flat();
let col = {};
hA.forEach((h, i) => { col[h.toLowerCase()] = i + 1 });
["type", "size", "color", "material", "length"].forEach(c => {
if (choiObj[prodObj[e.value][c]]) {
sh.getRange(e.range.rowStart, col[c]).setDataValidation(SpreadsheetApp.newDataValidation().requireValueInList(choiObj[prodObj[e.value][c]]).build()).offset(-1,0).setFontColor('#000000');
sh.showColumns(col[c])
} else {
sh.getRange(e.range.rowStart, col[c]).offset(-1,0).setFontColor('#ffffff');
sh.hideColumns(col[c]);
}
})
}
}
demo sheet
https://docs.google.com/spreadsheets/d/18guAXXjWIMDQilX8Z0Y4_Avogjs2ESMbrZY7Sb9TaxE/edit#gid=0
Sample Data
P1
Type
Size
Color
Material
Length
Kurta Pyjamas
No
Sizeethnic_1
Colorethnic_1
Materialethnic_3
Lengthethnic_1
Dhotis
Typethnic_1
No
Colorethnic_2
Materialethnic_2
No
Sherwani
No
No
Colorethnic_2
No
Lengthethnic_2
Men Pyjamas
Typeethnic_2
No
Colorethnic_2
No
No
Kurtas
No
Sizeethnic_2
Colorethnic_1
No
Lengthethnic_1
Ethnic Jackets
No
No
Colorethnic_1
No
No
Typethnic_1
Typeethnic_2
Sizeethnic_1
Sizeethnic_2
Colorethnic_1
Colorethnic_2
Materialethnic_3
Materialethnic_2
Lengthethnic_1
Lengthethnic_2
Mundu
Churidar
XS
XS
Beige
Green
Blended
Silk Blend
Above Knee
Short
Regular Dhoti
Regular Pyjama
S
S
Black
Grey
Cotton
Velevt
Ankle Length
Medium
Patiala
M
M
Blue
Maroon
Dupion
Viscose Rayon
Jodhpuri
L
L
Brown
Multi
Wool
Harem
XL
XL
Copper
Mustard
XXL
XXL
Cream
3XL
3XL
Gold
Suggestion
This may not be the cleanest code but you may try this implementation below. Instead of removing columns, it will clear the Sheet1 headers and their corresponding drop-downs on every new selection on the A2 drop-down.
NOTE: Since your sample data will increase in size overtime, this setup will need you to put the data into a separate sheet tab for a cleaner setup, such as this sample below:
Data1 sheet tab:
Data2 sheet tab:
UPDATED
Sample Script
var sh = SpreadsheetApp.getActiveSpreadsheet();
var selection = sh.getSheetByName("Sheet1").getRange("A2").getValue(); //Get the selection on the dropdowm on cell A2
var data1 = sh.getSheetByName("Data1").getDataRange().getDisplayValues();
var data2 = sh.getSheetByName("Data2").getDataRange().getDisplayValues();
function addHeaders(sheet, values) { //Adds the headers from Column C and beyond
var startCol = 3; //Column C
var endCol = startCol + values.length;
values.forEach(x => {
if(startCol <= endCol){
if(checkHeaderIfYesOrNo(x) == true)return;
sheet.getRange(1,startCol).setValue(x);
startCol += 1;
}
});
}
function onEdit(e) {
if(e.range.getA1Notation() != "A2")return;//Make sure to run onEdit function ONLY when cell A2 is edited/selected
var headers = [];
var headerValues = [];
var temp = [];
var counter = 0;
clean();
data1 = fixDuplicates();
//find selection name on data1
for(var x = 0; x< data1.length; x++){
var name = data1[x][0];
if(name == selection){
///get the headers & their values
data1[x].forEach(res => {
if(res != "" & res != selection){
var index1 = data1[x].indexOf(res);
var index2 = data2[0].indexOf(res);
headers.push([data1[0][index1]]);
for(var y=0; y< data2.length; y++){
if(data2[y][index2] != "" && data2[y][index2] != res){
temp.push("**"+res+"**"+data2[y][index2]); //place raw header data to a temporary variable
}
}
//Set the drop-down data of each headers
temp.forEach(raw => { //clean the temp array
if(raw.includes(res)){
var regex = /\*\*([^*]*(?:\*(?!\*)[^*]*)*)\*\*/g;
headerValues.push(raw.replace(regex, ""))
}
});
//Logger.log("Data of the \""+res+"\" header:\n"+headerValues);
//set data validation per header
counter += 1;
if(res.toLowerCase().includes("no"))return; //skip creating data validation for "No" header
if(res.toLowerCase().includes("yes")) return; //skip creating data validation for "Yes" header
sh.getSheetByName("Sheet1").getRange(2,2+counter).setDataValidation(SpreadsheetApp.newDataValidation().requireValueInList(headerValues).build());
headerValues = [];
}
});
}
}
addHeaders(sh.getSheetByName("Sheet1"), headers);
}
function clean(){ //Clean Sheet 1 on every edit
sh.getSheetByName("Sheet1").getRange('C2:Z').clearDataValidations();
sh.getSheetByName("Sheet1").getRange('C1:Z').clearContent();
}
function fixDuplicates(){
var temp = [];
var data1New = [];
var count = 1;
for(var x=0; x<data1.length; x++){
data1[x].forEach(findIt => {
if(findIt.toLowerCase().includes("yes") || findIt.toLowerCase().includes("no")){
temp.push(findIt+count);
count += 1;
}else{
temp.push(findIt);
}
})
data1New.push(temp);
temp=[];
}
return data1New;
}
function checkHeaderIfYesOrNo(h){
for(var x=0; x<data1.length; x++){
if(data1[x][0] == selection){
if(data1[x][data1[0].indexOf(h.toString())].toLowerCase().includes("yes")){
Logger.log(h +" contains Yes");
return null;
}else if(data1[x][data1[0].indexOf(h.toString())].toLowerCase().includes("no")){
Logger.log(h+" header will not be added as it has \"No\" value");
return true;
}else{
Logger.log("Skip the "+h +" header");
return null;
}
}
}
}
Sample Demonstration:
Sample Execution Log result for review:
I found this old question about synchronizing Google Sheets and Google Calendar and tried to download the template and then edit it accordingly.
When I try to run the script it prompts this error ReferenceError: calenderId is not defined.
Since I can only see one line, 9, where to to enter the calendarId I can not make the script work.
Did I miss a line in my example? I am aware that I need to write my cal-id in line 9.
// Script to synchronize a calendar to a spreadsheet and vice versa.
//
// See https://github.com/Davepar/gcalendarsync for instructions on setting this up.
//
// Set this value to match your calendar!!!
// Calendar ID can be found in the "Calendar Address" section of the Calendar Settings.
//var calendarId = '<your-calendar-id>#group.calendar.google.com';
var calendarId = 'Her er min kalender-adresse, naturligvis';
// Set the beginning and end dates that should be synced. beginDate can be set to Date() to use
// today. The numbers are year, month, date, where month is 0 for Jan through 11 for Dec.
var beginDate = new Date(1970, 0, 1); // Default to Jan 1, 1970
var endDate = new Date(2500, 0, 1); // Default to Jan 1, 2500
// Date format to use in the spreadsheet.
var dateFormat = 'M/d/yyyy H:mm';
var titleRowMap = {
'title': 'Title',
'description': 'Description',
'location': 'Location',
'starttime': 'Start Time',
'endtime': 'End Time',
'guests': 'Guests',
'color': 'Color',
'id': 'Id'
};
var titleRowKeys = ['title', 'description', 'location', 'starttime', 'endtime', 'guests', 'color', 'id'];
var requiredFields = ['id', 'title', 'starttime', 'endtime'];
// This controls whether email invites are sent to guests when the event is created in the
// calendar. Note that any changes to the event will cause email invites to be resent.
var SEND_EMAIL_INVITES = false;
// Setting this to true will silently skip rows that have a blank start and end time
// instead of popping up an error dialog.
var SKIP_BLANK_ROWS = false;
// Updating too many events in a short time period triggers an error. These values
// were successfully used for deleting and adding 240 events. Values in milliseconds.
var THROTTLE_SLEEP_TIME = 200;
var MAX_RUN_TIME = 5.75 * 60 * 1000;
// Special flag value. Don't change.
var EVENT_DIFFS_WITH_GUESTS = 999;
// Adds the custom menu to the active spreadsheet.
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{
name: "Update from Calendar",
functionName: "syncFromCalendar"
}, {
name: "Update to Calendar",
functionName: "syncToCalendar"
}
];
spreadsheet.addMenu('Calendar Sync', menuEntries);
}
// Creates a mapping array between spreadsheet column and event field name
function createIdxMap(row) {
var idxMap = [];
for (var idx = 0; idx < row.length; idx++) {
var fieldFromHdr = row[idx];
for (var titleKey in titleRowMap) {
if (titleRowMap[titleKey] == fieldFromHdr) {
idxMap.push(titleKey);
break;
}
}
if (idxMap.length <= idx) {
// Header field not in map, so add null
idxMap.push(null);
}
}
return idxMap;
}
// Converts a spreadsheet row into an object containing event-related fields
function reformatEvent(row, idxMap, keysToAdd) {
var reformatted = row.reduce(function(event, value, idx) {
if (idxMap[idx] != null) {
event[idxMap[idx]] = value;
}
return event;
}, {});
for (var k in keysToAdd) {
reformatted[keysToAdd[k]] = '';
}
return reformatted;
}
// Converts a calendar event to a psuedo-sheet event.
function convertCalEvent(calEvent) {
convertedEvent = {
'id': calEvent.getId(),
'title': calEvent.getTitle(),
'description': calEvent.getDescription(),
'location': calEvent.getLocation(),
'guests': calEvent.getGuestList().map(function(x) {return x.getEmail();}).join(','),
'color': calEvent.getColor()
};
if (calEvent.isAllDayEvent()) {
convertedEvent.starttime = calEvent.getAllDayStartDate();
var endtime = calEvent.getAllDayEndDate();
if (endtime - convertedEvent.starttime === 24 * 3600 * 1000) {
convertedEvent.endtime = '';
} else {
convertedEvent.endtime = endtime;
if (endtime.getHours() === 0 && endtime.getMinutes() == 0) {
convertedEvent.endtime.setSeconds(endtime.getSeconds() - 1);
}
}
} else {
convertedEvent.starttime = calEvent.getStartTime();
convertedEvent.endtime = calEvent.getEndTime();
}
return convertedEvent;
}
// Converts calendar event into spreadsheet data row
function calEventToSheet(calEvent, idxMap, dataRow) {
convertedEvent = convertCalEvent(calEvent);
for (var idx = 0; idx < idxMap.length; idx++) {
if (idxMap[idx] !== null) {
dataRow[idx] = convertedEvent[idxMap[idx]];
}
}
}
// Returns empty string or time in milliseconds for Date object
function getEndTime(ev) {
return ev.endtime === '' ? '' : ev.endtime.getTime();
}
// Determines the number of field differences between a calendar event and
// a spreadsheet event
function eventDifferences(convertedCalEvent, sev) {
var eventDiffs = 0 + (convertedCalEvent.title !== sev.title) +
(convertedCalEvent.description !== sev.description) +
(convertedCalEvent.location !== sev.location) +
(convertedCalEvent.starttime.toString() !== sev.starttime.toString()) +
(getEndTime(convertedCalEvent) !== getEndTime(sev)) +
(convertedCalEvent.guests !== sev.guests) +
(convertedCalEvent.color !== ('' + sev.color));
if (eventDiffs > 0 && convertedCalEvent.guests) {
// Use a special flag value if an event changed, but it has guests.
eventDiffs = EVENT_DIFFS_WITH_GUESTS;
}
return eventDiffs;
}
// Determine whether required fields are missing
function areRequiredFieldsMissing(idxMap) {
return requiredFields.some(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Returns list of fields that aren't in spreadsheet
function missingFields(idxMap) {
return titleRowKeys.filter(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Set up formats and hide ID column for empty spreadsheet
function setUpSheet(sheet, fieldKeys) {
sheet.getRange(1, fieldKeys.indexOf('starttime') + 1, 999).setNumberFormat(dateFormat);
sheet.getRange(1, fieldKeys.indexOf('endtime') + 1, 999).setNumberFormat(dateFormat);
sheet.hideColumns(fieldKeys.indexOf('id') + 1);
}
// Display error alert
function errorAlert(msg, evt, ridx) {
var ui = SpreadsheetApp.getUi();
if (evt) {
ui.alert('Skipping row: ' + msg + ' in event "' + evt.title + '", row ' + (ridx + 1));
} else {
ui.alert(msg);
}
}
// Updates a calendar event from a sheet event.
function updateEvent(calEvent, convertedCalEvent, sheetEvent){
var numChanges = 0;
sheetEvent.sendInvites = SEND_EMAIL_INVITES;
if (convertedCalEvent.starttime.toString() !== sheetEvent.starttime.toString() ||
getEndTime(convertedCalEvent) !== getEndTime(sheetEvent)) {
if (sheetEvent.endtime === '') {
calEvent.setAllDayDate(sheetEvent.starttime);
} else {
calEvent.setTime(sheetEvent.starttime, sheetEvent.endtime);
}
numChanges++;
}
if (convertedCalEvent.title !== sheetEvent.title) {
calEvent.setTitle(sheetEvent.title);
numChanges++;
}
if (convertedCalEvent.description !== sheetEvent.description) {
calEvent.setDescription(sheetEvent.description);
numChanges++;
}
if (convertedCalEvent.location !== sheetEvent.location) {
calEvent.setLocation(sheetEvent.location);
numChanges++;
}
if (convertedCalEvent.color !== ('' + sheetEvent.color)) {
if (sheetEvent.color > 0 && sheetEvent.color < 12) {
calEvent.setColor('' + sheetEvent.color);
numChanges++;
}
}
if (convertedCalEvent.guests !== sheetEvent.guests) {
var guestCal = calEvent.getGuestList().map(function (x) {
return {
email: x.getEmail(),
added: false
};
});
var sheetGuests = sheetEvent.guests || '';
var guests = sheetGuests.split(',').map(function (x) {
return x ? x.trim() : '';
});
// Check guests that are already invited.
for (var gIx = 0; gIx < guestCal.length; gIx++) {
var index = guests.indexOf(guestCal[gIx].email);
if (index >= 0) {
guestCal[gIx].added = true;
guests.splice(index, 1);
}
}
guests.forEach(function (guest) {
if (guest) {
calEvent.addGuest(guest);
numChanges++;
}
});
guestCal.forEach(function (guest) {
if (!guest.added) {
calEvent.removeGuest(guest.email);
numChanges++;
}
});
}
// Throttle updates.
Utilities.sleep(THROTTLE_SLEEP_TIME * numChanges);
return numChanges;
}
// Synchronize from calendar to spreadsheet.
function syncFromCalendar() {
console.info('Starting sync from calendar');
// Get calendar and events
var calendar = CalendarApp.getCalendarById(calendarId);
var calEvents = calendar.getEvents(beginDate, endDate);
// Get spreadsheet and data
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var eventFound = new Array(data.length);
// Check if spreadsheet is empty and add a title row
var titleRow = [];
for (var idx = 0; idx < titleRowKeys.length; idx++) {
titleRow.push(titleRowMap[titleRowKeys[idx]]);
}
if (data.length < 1) {
data.push(titleRow);
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
setUpSheet(sheet, titleRowKeys);
}
if (data.length == 1 && data[0].length == 1 && data[0][0] === '') {
data[0] = titleRow;
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
setUpSheet(sheet, titleRowKeys);
}
// Map spreadsheet headers to indices
var idxMap = createIdxMap(data[0]);
var idIdx = idxMap.indexOf('id');
// Verify header has all required fields
if (areRequiredFieldsMissing(idxMap)) {
var reqFieldNames = requiredFields.map(function(x) {return titleRowMap[x];}).join(', ');
errorAlert('Spreadsheet must have ' + reqFieldNames + ' columns');
return;
}
// Array of IDs in the spreadsheet
var sheetEventIds = data.slice(1).map(function(row) {return row[idIdx];});
// Loop through calendar events
for (var cidx = 0; cidx < calEvents.length; cidx++) {
var calEvent = calEvents[cidx];
var calEventId = calEvent.getId();
var ridx = sheetEventIds.indexOf(calEventId) + 1;
if (ridx < 1) {
// Event not found, create it
ridx = data.length;
var newRow = [];
var rowSize = idxMap.length;
while (rowSize--) newRow.push('');
data.push(newRow);
} else {
eventFound[ridx] = true;
}
// Update event in spreadsheet data
calEventToSheet(calEvent, idxMap, data[ridx]);
}
// Remove any data rows not found in the calendar
var rowsDeleted = 0;
for (var idx = eventFound.length - 1; idx > 0; idx--) {
//event doesn't exists and has an event id
if (!eventFound[idx] && sheetEventIds[idx - 1]) {
data.splice(idx, 1);
rowsDeleted++;
}
}
// Save spreadsheet changes
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
if (rowsDeleted > 0) {
sheet.deleteRows(data.length + 1, rowsDeleted);
}
}
// Synchronize from spreadsheet to calendar.
function syncToCalendar() {
console.info('Starting sync to calendar');
var scriptStart = Date.now();
// Get calendar and events
var calendar = CalendarApp.getCalendarById(calendarId);
if (!calendar) {
errorAlert('Cannot find calendar. Check instructions for set up.');
}
var calEvents = calendar.getEvents(beginDate, endDate);
var calEventIds = calEvents.map(function(val) {return val.getId();});
// Get spreadsheet and data
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
if (data.length < 2) {
errorAlert('Spreadsheet must have a title row and at least one data row');
return;
}
// Map headers to indices
var idxMap = createIdxMap(data[0]);
var idIdx = idxMap.indexOf('id');
var idRange = range.offset(0, idIdx, data.length, 1);
var idData = idRange.getValues()
// Verify header has all required fields
if (areRequiredFieldsMissing(idxMap)) {
var reqFieldNames = requiredFields.map(function(x) {return titleRowMap[x];}).join(', ');
errorAlert('Spreadsheet must have ' + reqFieldNames + ' columns');
return;
}
var keysToAdd = missingFields(idxMap);
// Loop through spreadsheet rows
var numAdded = 0;
var numUpdates = 0;
var eventsAdded = false;
for (var ridx = 1; ridx < data.length; ridx++) {
var sheetEvent = reformatEvent(data[ridx], idxMap, keysToAdd);
// If enabled, skip rows with blank/invalid start and end times
if (SKIP_BLANK_ROWS && !(sheetEvent.starttime instanceof Date) &&
!(sheetEvent.endtime instanceof Date)) {
continue;
}
// Do some error checking first
if (!sheetEvent.title) {
errorAlert('must have title', sheetEvent, ridx);
continue;
}
if (!(sheetEvent.starttime instanceof Date)) {
errorAlert('start time must be a date/time', sheetEvent, ridx);
continue;
}
if (sheetEvent.endtime !== '') {
if (!(sheetEvent.endtime instanceof Date)) {
errorAlert('end time must be empty or a date/time', sheetEvent, ridx);
continue;
}
if (sheetEvent.endtime < sheetEvent.starttime) {
errorAlert('end time must be after start time for event', sheetEvent, ridx);
continue;
}
}
// Ignore events outside of the begin/end range desired.
if (sheetEvent.starttime > endDate) {
continue;
}
if (sheetEvent.endtime === '') {
if (sheetEvent.starttime < beginDate) {
continue;
}
} else {
if (sheetEvent.endtime < beginDate) {
continue;
}
}
// Determine if spreadsheet event is already in calendar and matches
var addEvent = true;
if (sheetEvent.id) {
var eventIdx = calEventIds.indexOf(sheetEvent.id);
if (eventIdx >= 0) {
calEventIds[eventIdx] = null; // Prevents removing event below
addEvent = false;
var calEvent = calEvents[eventIdx];
var convertedCalEvent = convertCalEvent(calEvent);
var eventDiffs = eventDifferences(convertedCalEvent, sheetEvent);
if (eventDiffs > 0) {
// When there are only 1 or 2 event differences, it's quicker to
// update the event. For more event diffs, delete and re-add the event. The one
// exception is if the event has guests (eventDiffs=99). We don't
// want to force guests to re-confirm, so go through the slow update
// process instead.
if (eventDiffs < 3 && eventDiffs !== EVENT_DIFFS_WITH_GUESTS) {
numUpdates += updateEvent(calEvent, convertedCalEvent, sheetEvent);
} else {
addEvent = true;
calEventIds[eventIdx] = sheetEvent.id;
}
}
}
}
console.info('%d updates, time: %d msecs', numUpdates, Date.now() - scriptStart);
if (addEvent) {
var newEvent;
sheetEvent.sendInvites = SEND_EMAIL_INVITES;
if (sheetEvent.endtime === '') {
newEvent = calendar.createAllDayEvent(sheetEvent.title, sheetEvent.starttime, sheetEvent);
} else {
newEvent = calendar.createEvent(sheetEvent.title, sheetEvent.starttime, sheetEvent.endtime, sheetEvent);
}
// Put event ID back into spreadsheet
idData[ridx][0] = newEvent.getId();
eventsAdded = true;
// Set event color
if (sheetEvent.color > 0 && sheetEvent.color < 12) {
newEvent.setColor('' + sheetEvent.color);
}
// Throttle updates.
numAdded++;
Utilities.sleep(THROTTLE_SLEEP_TIME);
if (numAdded % 10 === 0) {
console.info('%d events added, time: %d msecs', numAdded, Date.now() - scriptStart);
}
}
// If the script is getting close to timing out, save the event IDs added so far to avoid lots
// of duplicate events.
if ((Date.now() - scriptStart) > MAX_RUN_TIME) {
idRange.setValues(idData);
}
}
// Save spreadsheet changes
if (eventsAdded) {
idRange.setValues(idData);
}
// Remove any calendar events not found in the spreadsheet
var numToRemove = calEventIds.reduce(function(prevVal, curVal) {
if (curVal !== null) {
prevVal++;
}
return prevVal;
}, 0);
if (numToRemove > 0) {
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Delete ' + numToRemove + ' calendar event(s) not found in spreadsheet?',
ui.ButtonSet.YES_NO);
if (response == ui.Button.YES) {
var numRemoved = 0;
calEventIds.forEach(function(id, idx) {
if (id != null) {
calEvents[idx].deleteEvent();
Utilities.sleep(THROTTLE_SLEEP_TIME);
numRemoved++;
if (numRemoved % 10 === 0) {
console.info('%d events removed, time: %d msecs', numRemoved, Date.now() - scriptStart);
}
}
});
}
}
}
// Set up a trigger to automatically update the calendar when the spreadsheet is
// modified. See the instructions for how to use this.
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('syncToCalendar')
.forSpreadsheet(ss)
.onEdit()
.create();
}
// Delete the trigger. Use this to stop automatically updating the calendar.
function deleteTrigger() {
// Loop over all triggers.
var allTriggers = ScriptApp.getProjectTriggers();
for (var idx = 0; idx < allTriggers.length; idx++) {
if (allTriggers[idx].getHandlerFunction() === 'syncToCalendar') {
ScriptApp.deleteTrigger(allTriggers[idx]);
}
}
}
The ReferenceError: calenderId is not defined error message you are getting is due to the fact that you are declaring calenderId instead of calendarId and you are using calendarId all over your code.
So in order to fix your error you might want to check the line at which you are declaring the variable and declare it with the appropriate name.