Delete function works but sends error message - javascript

I've been using this function to erase certain rows depending on the name of a cell in a column, for some reason it works, but after it finishes it just gives me an error message.
Exception: Service Spreadsheets failed while accessing document with id #################################################.
function erase() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var range = 'J:J';
var object = 'Delete';
var name = 'Roster';
var sheet = spreadsheet.getSheetByName(name);
var row = sheet.getRange(range);
var value = row.getValues();
for (var i = value.length - 1; i >= 0; i--)
if (value[0, i] == object)
sheet.deleteRow(i + 1);
}

This looks to be a known issue from Google's side which has been reported on Issue Tracker. I suggest you star the issue from here and eventually add a comment saying that you are affected by it.
As a possible workaround, you can try adding SpreadsheetApp.flush() before this line here var row = sheet.getRange(range);.

I'm not familiar with google sheets specifically, but just with general syntax knowledge, I feel like the issue would surround, var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();. Are you sure you arn't supposed to put anything inside the getActiveSreadshee(-here-)??

Related

Google Script return value from one sheet to another sheet

This is my first Google Script and I'm struggling a little bit while trying to bring back the values from a specific cell & sheet to another sheet.
I have a total of 18 columns, being the first one the ID which is going to be the input that the user would need to add in order to retrieve the data from one sheet to another. As the first one is the ID, and will be already be inputted by the user, I would need to retrieve the data from columns 2 to 18
Here is my code:
function SearchID() {
var columnIndex = 0;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Manual Costs App"); // App sheet
var str = formS.getRange("D6").getValue(); // The ID to search will be inputted here
var dataS = ss.getSheetByName("Inputs").getDataRange().getValues(); // Retrieving the data based on the user input
for (var i = 0; i<=dataS.length; i++) {
var row = dataS[i];
if (row[columnIndex] == str) {
formS.getRange("D9").setValue(row[1]);
formS.getRange("D13").setValue(row[2]);
formS.getRange("D14").setValue(row[3]);
formS.getRange("D15").setValue(row[4]);
formS.getRange("D16").setValue(row[5]);
formS.getRange("D18").setValue(row[6]);
formS.getRange("D19").setValue(row[7]);
formS.getRange("D20").setValue(row[8]);
formS.getRange("D21").setValue(row[9]);
formS.getRange("D22").setValue(row[10]);
formS.getRange("D23").setValue(row[11]);
formS.getRange("D25").setValue(row[12]);
formS.getRange("D26").setValue(row[13]);
formS.getRange("D27").setValue(row[14]);
formS.getRange("D28").setValue(row[15]);
formS.getRange("D29").setValue(row[16]);
formS.getRange("D30").setValue(row[17]);
break;
}
}
}
The link to a sample spreadsheet of what I'm building is here
Update: Everything is fixed now! What I did was removing the space in the for loop. After that, it retrieved the data but a TypeError: Cannot read property '0'. Also solved it adding a break after the loop to avoid it.
It's a type in your for loop, notice how spelled length:
for (var i = 1; i <= values.lenght; i++)
You also don't want to set values line by line like you do, get a longer range and set the values with setValues() rather than setValue(). There's quite a bit of refactoring to do there actually.
The user did found the answer and updated it on the question.
Posting here as community wiki so it can be seen more clearly.
User's answer:
Update: Everything is fixed now! What I did was removing the space in the for loop. After that, it retrieved the data but a TypeError: Cannot read property '0'. Also solved it adding a break after the loop to avoid it.
function SearchID() {
var columnIndex = 0;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Manual Costs App"); // App sheet
var str = formS.getRange("D6").getValue(); // The ID to search will be inputted here
var dataS = ss.getSheetByName("Inputs").getDataRange().getValues(); // Retrieving the data based on the user input
for (var i = 0; i<=dataS.length; i++) {
var row = dataS[i];
if (row[columnIndex] == str) {
formS.getRange("D9").setValue(row[1]);
formS.getRange("D13").setValue(row[2]);
formS.getRange("D14").setValue(row[3]);
formS.getRange("D15").setValue(row[4]);
formS.getRange("D16").setValue(row[5]);
formS.getRange("D18").setValue(row[6]);
formS.getRange("D19").setValue(row[7]);
formS.getRange("D20").setValue(row[8]);
formS.getRange("D21").setValue(row[9]);
formS.getRange("D22").setValue(row[10]);
formS.getRange("D23").setValue(row[11]);
formS.getRange("D25").setValue(row[12]);
formS.getRange("D26").setValue(row[13]);
formS.getRange("D27").setValue(row[14]);
formS.getRange("D28").setValue(row[15]);
formS.getRange("D29").setValue(row[16]);
formS.getRange("D30").setValue(row[17]);
break;
}
}
}

How to check if a sheet cells includes errors or not?

I am using this script function to check if my cell functions in sheet has any errors or not.
Here is the code but it does not seems to be working. It keeps on saying no error when i have an error in a cell
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourcename = "Sheet1";
var source = ss.getSheetByName(sourcename);
var cell = source.getRange("A1:AG30");
function isError2(cell) {
const errorValues = ["#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A","#ERROR!"];
if (errorValues.includes(cell) != true) {
Logger.log("no error");
} else{
Logger.log("some error");
}
}
function isError2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourcename = "Sheet1";
var source = ss.getSheetByName(sourcename);
var cell = source.getRange("A1:AG30");
const errorValues = ["#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A","#ERROR!"];
if (errorValues.includes(cell) != true) {
Logger.log("no error");
} else{
Logger.log("some error");
}
}
Updated the approach but still having no luck with the desired output
var mysheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var sheet1 = SpreadsheetApp.setActiveSheet(mysheet);
function findErrors(sheet) {
const errorValues = ["#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A","#ERROR!"];
var singleSheetArray = [];
var name = sheet1.getName();
// how many cells in the sheet currently
var maxRows = sheet1.getMaxRows();
var maxCols = sheet1.getMaxColumns();
var totalCells = maxRows * maxCols;
// how many cells have data in them
var r = sheet1.getLastRow();
var c = sheet1.getLastColumn();
var data_counter = r * c;
if (data_counter !== 0) {
var dataRange = sheet1.getRange(1,1,r,c);
var dataValues = dataRange.getValues();
dataValues.forEach(function(row) {
row.forEach(function(cell) {
if ((errorValues.indexOf(cell) === -1) ) {
SpreadsheetApp.getUi().alert("no errors in "+cell);
data_counter --;
}
});
});
}
}
Problem
Unable to check whether the cell has an error
Explanation
The issue you are facing is a simple type mismatch. getRange() method returns an instance of Range, while you try to compare it to a member of a errorValues array, which consists of strings. Therefore, errorValues.includes(cell) will always be false, hence first block of the conditional statement executing.
Solution
Use getValues() on the range, it will return you a 2-dimensional array of values. If you are only interested in one row (which you probably are), extract it and loop over the cells with some (or every) method, doing the same comparison.
Notes
On using global variables in custom functions and in GAS in general. You can use them, GAS environment is a JavaScript runtime with a convenience layer that simplifies working with Google APIs, nearly everything that's valid in JS is valid here. That being said, do treat global variables as if they don't exist - unless you know exactly what you are doing.
References
getRange method reference
getValues method reference
Custom functions guide
every method reference on MDN (see some there)
Try to move the 4 variables inside your function. Apps script does not support global variables. So the function does not recognize the var cell.
EDIT: Detect formula errors in Google Sheets using Script

Cannot call method "getEditResponseUrl" of undefined on Google Apps Script bound to Sheet when opening form using form ID

I have this function which works but it gets all responses.
function setEditUrl(ss, createDateColumn)
{
var formURL = 'https://docs.google.com/forms/d/101bMiRw9TQaGbdDc4U_tLAD0QzicqejM9qXOEwJPQKU/viewform';
var urlColumn = createDateColumn-2;
var data = ss.getDataRange().getValues();
var form = FormApp.openByUrl(formURL);
for(var i = 2; i < data.length; i++)
{
if(data[i][0] != '' && data[i][urlColumn-1] == '')
{
var timestamp = data[i][0];
var formSubmitted = form.getResponses(timestamp);
if(formSubmitted.length < 1) continue;
var editResponseUrl = formSubmitted[0].getEditResponseUrl();
ss.getRange(i+1, urlColumn).setValue(editResponseUrl);
}//end of if
}//end of for
return;
}// This is the end of the setEditUrl function
As the spreadsheet gets larger I am concerned with performance lag so I want to streamline it and replace the function with one like the one below which just gets the editURL for the last response and only if the sheet cell is empty
function setGoogleFormURL(ss, lastRowInx, createDateColumn)
{
var urlColumn = createDateColumn-2;
if (ss.getRange(lastRowInx, urlColumn).getValue() == "") // so that subsequent edits to Google Form don't overwrite editResponseURL
{
var form = FormApp.openById('101bMiRw9TQaGbdDc4U_tLAD0QzicqejM9qXOEwJPQKU');
var formResponses = form.getResponses();
var lastResponseIndex = form.getResponses.length-1;
var lastResponse = formResponses[lastResponseIndex];
var editResponseUrl = lastResponse.getEditResponseUrl();
var createEditResponseUrl = ss.getRange(lastRowInx, urlColumn);
createEditResponseUrl.setValue(editResponseUrl);
}
else{} //do nothing
however this seems to break on the getEditResponseUrl. I am getting the following error TypeError: Cannot call method "getEditResponseUrl" of undefined. (line 100, file "Code").
I used #SandyGood 's answer to this post as a reference. I wonder though if her observation about the event trigger is why this is borking. This is the onFormSubmit function I am using to call this and other fucntions.
function onFormSubmit(e)
{
var ss = SpreadsheetApp.getActiveSheet();
var lastRowInx = ss.getLastRow(); // Get the row number of the last row with content
var createDateColumn = ss.getMaxColumns(); //CreateDateColumn is currently in AX (Column 50) which is the last/max column position
var createDate = setCreateDate(ss, lastRowInx, createDateColumn);
var trackingNumber = setTrackingNumber(ss, lastRowInx, createDateColumn);
//var editURL = setEditUrl(ss, createDateColumn);
var editResponseURL = setGoogleFormURL(ss, lastRowInx, createDateColumn);
}//This is the end of onFormSubmit
I also found a whole bunch of sources 234where they were looking use the URL to append to an email, were more complex than my use case, or were unanswered. I also found some solutions for getting the EditURL by binding the script to the form but since I want to store the value on the sheet it needs to be bound to the sheet rather than the form.
UPDATE:
Okay so I tried to bind my script to the form instead of the sheet which allowed me to see the URL but now I have the problem in reverse where the form can't find the spreadsheet methods like .getMaxColumns TypeError: Cannot find function getMaxColumns in object Spreadsheet. (line 40, file "Code") AND .getActiveRange Cannot find method getActiveRange(number). (line 48, file "Code").
Here is the code on the form side
function onFormSubmit(e)
{
var form = FormApp.getActiveForm();
var activeFormUrl = form.getEditUrl();
var ss = SpreadsheetApp.openById(form.getDestinationId());
var createDateColumn = ss.getMaxColumns(); //CreateDateColumn is currently in AY (Column 51) which is the last/max column position
var urlColumn = createDateColumn-1; //urlColumn is currently in AX (Column 50) Calculating using it's relative position to createDateColumn Position
Logger.log(activeFormUrl, createDateColumn, urlColumn);
var checkLog1 = Logger.getLog();
Logger.clear();
if (ss.getActiveRange(urlColumn).getValue() == "") // so that subsequent edits to Google Form don't overwrite editResponseURL
{
var editResponseURL = setGoogleFormEditUrl(ss, createDateColumn, activeFormUrl);
var createEditResponseUrl = ss.getActiveRange(urlColumn);
createEditResponseUrl.setValue(activeFormUrl);
}
else
{
if (ss.getActiveRange(urlColumn).getValue() != activeFormUrl)
{
Logger.log("Something went wrong - URL doesn't match")
Logger.log(ss.getActiveRange(urlColumn).getValue());
var checkLog2 = Logger.getLog();
}
else {}//do nothing
}
}//This is the end of the onFormSubmit function
So I am wondering how I can pass a variable between the form and the sheet. Can I somehow read the form log programmically from the sheet? Can I append the value to the form response array (This would mean a few other edits to the referenced columns but could work). Thoughts #Gerneio , #SandyGood , Anyone else?
UPDATE 2:
There seemed to be a conflict with using both the methods from the FormApp and the SpreadsheetApp within the same function.
The solution that worked for me was to modularize the spreadsheet functions out (except the getActiveSheet) and to leave the getEditResponseURL method within the onFormSubmit Function.
The code snippet can be found posted here.
I'd suggest trying to use the onFormSubmit(e) on the form side.
function onFormSubmit(e)
{
var form = e.source;
var response = e.response;
var sheet = SpreadsheetApp.openById(form.getDestinationId());
var editUrl = response.getEditResponseUrl();
Logger.log(editUrl); // check the logger to see what results you are getting now
// Then do whatever operations you need to do...
}
Update:
I'm not so sure why you are having so many problems with this, but I can tell you for sure that it can be done from either side, the Form or Spreadsheet. I just put together a working example with code written on the Spreadsheet side, none what-so-ever on the Form side. Check it out:
function onFormSubmit(e)
{
var rng = e.range;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var fUrl = ss.getFormUrl();
var f = FormApp.openByUrl(fUrl);
var rs = f.getResponses();
var r = rs[rs.length - 1]; // Get last response made
var c = getCellRngByCol(rng, 'Edit Response URL');
c.setValue(r.getEditResponseUrl());
}
// Specific for a form submit trigger
// Pass e.range and the name of the column
// to return a single cell
function getCellRngByCol(rng, col)
{
var aRng = SpreadsheetApp.getActiveSheet().getDataRange();
var hRng = aRng.offset(0, 0, 1, aRng.getNumColumns()).getValues();
var colIndex = hRng[0].indexOf(col);
return SpreadsheetApp.getActiveSheet().getRange(rng.getRow(), colIndex + 1);
}
There were a few small hiccups that I ran into. Firstly, make sure to setup the trigger accordingly. I highly recommend setting up immediate notifications of failures. Secondly, even though the function will rely on the event that is passed, manually run the onFormSubmit(e) method at least once before submitting a form. It will check to see if your script needs any authorization and will request if needed. I'd also recommend that you open up a new form, link a fresh new spreadsheet, and test this code to make sure it works. Then mold the above code to fit your needs.
If you can't get it, then I'll share a working example.
There seemed to be a conflict with using both the methods from the FormApp and the SpreadsheetApp within the same function.
The solution that worked for me was to modularize the spreadsheet functions out (except the getActiveSheet) and to leave the getEditResponseURL method within the onFormSubmit Function.
The code snippet can be found posted here.

Google Spreadsheet On change write log to another sheet

please can anyone help with one problem in google spreadsheet?
After changing value in one concrete collumn in sheet "Venues", I would like to write log about name and time, when this value was changed. But I can't really realize , if I am working with spreadsheet "Venues" or some other. I am not very into class structure of google API for Spreadsheet. So can anyone help with it?
I need:
run eventhandler on event when value in appropriate column in appropriate sheet ("Venues") is changed
get value from collumn name from this sheet
get actual time
write name and time to another sheet called "status_history" to last row (like append)
My hard try to write something: (but that is really bad code)
function onEdit(event)
{
var sheet = event.source.getActiveSheet();
var cell = sheet.getActiveCell();
var cellR = cell.getRow();
var cellC = cell.getColumn();
var cellValue = cell.getValue();
var cellCName = cell.getColumn()-1; //column with names
var name = sheet.getRange(cellR, cellCName).getValue();//get name
var active_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
if(sheet.getName() == "Venues"){
if(cellC == 5 /* if correct collumn was changed */){
var output_sheet = active_spreadsheet.getSheetByName("status_history");
var lastRow = output_sheet.getLastRow();
var lastRange = output_sheet.getRange(lastRow, 1)
//HERE: write value: name
var lastRow = output_sheet.getLastRow();
var lastRange = output_sheet.getRange(lastRow, 2)
//HERE: write value: time
}
}
}
You were getting there. Just a couple of tweaks needed.
With onEdit functions, you need to keep things fast, since they get invoked so often.
Rely on the event information as much as you can, avoiding calls to Google Apps services.
If you must use a service, do it only when you absolutely need to - for example, wait until you are past the if statements that tell whether you are in a cell you want to log before calling SpreadsheetApp.getActiveSpreadsheet().
The API is rich, so look for functions that will let you reduce the number of system calls you make - see how appendRow() replaced multiple statements, for example.
Here's your function after a code inspection:
function onEdit(event) {
var sheet = event.range.getSheet();
if(sheet.getName() == "Venues"){
// correct sheet
var cell = event.range;
//var cellR = cell.getRow(); // not used at this time
var cellC = cell.getColumn();
var cellValue = event.value;
if (cellC == 5) {
// correct column
var name = cell.offset(0,-1).getValue(); // get name, 1 column to left
var time = new Date(); // timestamp
var active_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var output_sheet = active_spreadsheet.getSheetByName("status_history");
output_sheet.appendRow([name,time]);
}
}
}
You could make it more flexible and portable by using column names to test conditions. Take a look at Adam's answer here.

Dynamic Spreadsheet using HTML + JavaScript only

So I've got a couple of questions. I'm attempting to create a dynamic spreadsheet, with the capabilities of having values inserted into it as well as (and only needed) being able to process the SUM() formula. The problem I've run into right now, is when inserting a value.
Firstly allow em to show the JS I have for the insertion. The HTML table is using id values of 1_1,1_2 etc.
EDIT: Fixed the var column= document.getElementById(insert); line, however it doesn't seem to want to commit any inserted value. Shouldn't it simply be a return statement to commit a change to the html field?
function insertValue() {
var v = document.getElementById("submitText");
v = v.value;
var row = document.getElementById("row");
row = row.value;
var col = document.getElementById("col");
col = col.value;
var insert = row + "_" + col;
insert = insert.toString();
var col = document.getElementById("col");
EDIT:: return column.value = v;
} //end function
When I'm attempting to take the insert variable and use it as the id value, it breaks the whole program. Should I be casting the insert var to something other then a string?
I assume
var column.getElementById(insert);
should be
var column = document.getElementById(insert);
Your line of code is invalid.
function insertValue() {
var v = document.getElementById("submitText");
v = v.value;
var row = document.getElementById("row");
row = row.value;
var col = document.getElementById("col");
col = col.value;
var insert = row + "_" + col;
// Don't really think this is necessary, so probably remove it.
//insert = insert.toString();
var column = document.getElementById(insert); // Compare this line to yours
column.value = v;
} //end function
The reason of having error is because you're not writing it correctly.
var column.getElementById(insert); is wrong syntax. var is supposed to be followed by a variable name, and you can assign an initial value to it.
It is surprising that though you got it correct in the beginning but fail to do so later.
But the next time you have an error in your JavaScript code, check the browser's JavaScript Console. It can probably be opened using F12, and inside you will see the error is shown. Then you will not need to always come up and ask a question for every single problem you met. :)

Categories